I'm struggling to find the time complexity of this function:
void foo(int n) {
int i, m = 1;
for (i = 0; i < n; i++) {
m *= n; // (m = n^n) ??
}
while (m > 1) {
m /= 3;
}
}
Well, the first for iteration is clearly O(n^n), the explanation to it is because m started with value 1, and multiplies itself n times.
Now, we start the while loop with m = n^n and we divide it every time by 3.
which means, (I guess), log(n^n).
Assuming I got it right up till now, I'm not sure if I need to sum or multiply, but my logic says I need to sum them, because they are 'odd' to each other.
So my assumption is: O(n^n) + O(log(n^n)) = O(n^n) Because if n is quite big, we can just refrain from O(log(n^n)).
Well, I really made many assumptions here, and I hope that makes sense. I'd love to hear your opinions about the time complexity of this function.
Theoretically, time complexity is O(n log n) because:
for (i=0; i<n; i++)
m *= n;
this will be executed n times and in the end m=n^n
Then this
while (m>1)
m /= 3;
will be executed log3(n^n) times which is n * log3(n):
P.S. But this is only if you count number of operations. In real life it takes much more time to calculate n^n because the numbers become too big. Also your function will overflow when you will be multiplying such big numbers and most probably you will be bounded by the maximum number of int (in which case the complexity will be O(n))
With foo(int n) and 32-bit int, n cannot exceed the magnitude of 10, else m *= n overflows.
Given such a small range that n works, the O() seems moot. Even with 64-bit unsigned m, n <= 15.
So I suppose O(n lg(n)) is technically correct, but given the constraints of int, suspect code took more time to do a single printf() than iterate through foo(10). IOWs it is practically O(1).
unsigned long long foo(int n) {
unsigned long long cnt = 0;
int i;
unsigned long long m = 1;
for (i = 0; i < n; i++) {
if (m >= ULLONG_MAX/n) exit(1);
m *= n; // (m = n^n) ??
cnt++;
}
while (m > 1) {
m /= 3;
cnt++;
}
return cnt;
}
And came up with
1 1
2 3
3 6
4 9
5 12
6 16
7 19
8 23
9 27
10 31
11 35
12 39
13 43
14 47
15 52
Related
The goal of this program is to find the smallest number that can be divided by the numbers 1 to 20 without any remainders. The code is working but it takes 33 seconds. Can I improve it so that it can be faster? How?
#include <stdio.h>
int main(){
int number = 19, i, k;
label:
number++;
k = 0;
for (i = 1; i <= 20; i++){
if (number%i == 0){
k++;
}
}
if (k != 20){
goto label;
}
printf("%d\n", number);
return 0;
}
#include <stdio.h>
/* GCD returns the greatest common divisor of a and b (which must be non-zero).
This algorithm comes from Euclid, Elements, circa 300 BCE, book (chapter)
VII, propositions 1 and 2.
*/
static unsigned GCD(unsigned a, unsigned b)
{
while (0 < b)
{
unsigned c = a % b;
a = b;
b = c;
}
return a;
}
int main(void)
{
static const unsigned Limit = 20;
unsigned LCM = 1;
/* Update LCM to the least common multiple of the LCM so far and the next
i. The least common multiple is obtained by multiplying the numbers
and removing the duplicated common factors by dividing by the GCD.
*/
for (unsigned i = 1; i <= Limit; ++i)
LCM *= i / GCD(LCM, i);
printf("The least common multiple of numbers from 1 to %u is %u.\n",
Limit, LCM);
}
Change
int number = 19 ;
to
int number = 0 ;
then:
number++;
to
number += 20 ;
is an obvious improvement that will have a significant impact even if it is still a somewhat naive brute force approach.
At onlinegdb.com your algorithm took 102 seconds to run whereas this change runs in less that one second and produces the same answer.
The initial product of primes value suggested in a comment will provide a further improvement.
You need to multiply all the least common multiples together, but omit numbers that could be multiplied to get any of the others. This translates to multiply by all primes less than N with each prime number raised to the highest power <= N.
const unsigned primes[] = {
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47
};
unsigned long long answer(unsigned n){ //for your example n=20
if (n>46) return 0; //will overflow 64 bit unsigned long long
unsigned long long tmp, ret = 1;
for (unsigned i = 0; primes[i]<=n;++i){ //each prime less than n
tmp = primes[i];
while ((tmp*primes[i])<=n) //highest power less than n
tmp *= primes[i];
ret *= tmp;
}
return ret;
}
usage: printf("%llu", answer(20));
If my math/code is right this should be fast and cover numbers up to 46. If your compiler supports unsigned __int128 it can be modified to go up to 88.
Explanation:
TLDR version: all numbers are either prime or can be made by multiplying primes.
To get the least common multiple of a set of numbers you break each
number into it's prime multiples and multiply the highest number of
each prime together.
Primes less than 20:
2,3,5,7,11,13,17,19
Non primes under 20:
4 = 2*2
6 = 2*3
8 = 2*2*2
9 = 3*3
10 = 2*5
12 = 2*2*3
14 = 2*2*7
15 = 3*5
16 = 2*2*2*2
18 = 2*3*3
20 = 2*2*5
From this we see that the maximum number of 2s is 4 and the maximum
number of 3s is 2.
2 to the 4th <= 20
3 squared <= 20
All powers >1 of the remaining
primes are greater than 20.
Therefore you get:
2*2*2*2*3*3*5*7*11*13*17*19
Which is what you would see if you watched the tmp variable in a
debugger.
Another reason this is faster is that it avoids modulus and division
(It's expensive on a lot of systems)
Here's a way to do it without defining primes or divisions (except for a single sqrt), using a Sieve of Eratosthenes (circa 200 BCE).
I mark composites with 1, and primes^x with -1. Then i just loop over the array of numbers from sqrt(n) to n and pull out the remaining primes and max power primes.
#include <stdio.h>
#include <math.h>
#define n 20
int main()
{
int prime [100]={0};
int rootN = sqrt(n);
unsigned long long inc,oldInc;
int i;
for (i=2; i<rootN; i++)
{
if (prime[i] == 1) continue;
//Classic Sieve
inc = i*i;
while (inc < n)
{
prime[inc] = 1;
inc += i;
}
//Max power of prime
oldInc = i;
inc = i * i;
while (inc < n)
{
prime[inc] = 1;
oldInc=inc;
inc *= i;
}
prime[oldInc] = -1;
prime[i] = 1;
}
inc = 1;
for(i=rootN; i<n; i++)
{
if (prime[i] == 0 || prime[i] == -1)
{
inc = inc * i;
}
}
printf("%llu",inc);
return 0;
}
I am currently learning about Big O Notation running times. I try to calculate the time complexity of some code:
int i = 1;
int n = 3; //this variable is unknown
int j;
while (i<=n)
{
for (j = 1; j < i; j++)
printf_s("*");
j *= 2;
i *= 3;
}
I think that complexity of this code is О(log n). But even if it is correct, I can`t explain why.
The time complexity is not O(log n), it is O(n).
We can calculate that in a structured way. First we examine the inner loop:
for (j = 1; j < i; j++)
printf_s("*");
Here j iterates from 1 to i. So that means that for a given i, it will take i-1 steps.
Now we can look at the outer loop, and we can abstract away the inner loop:
while (i<=n)
{
// ... i-1 steps ...
j *= 2;
i *= 3;
}
So each iteration of the while loop, we perform i-1 steps. Furthermore each iteration the i doubles, until it is larger than n. We thus can say that the number of steps of this algorithm is:
log3 n
---
\ k
/ 3 - 1
---
k=0
We here use k as an extra variable that starts at 0 and each time increments. It thus counts how many times we perform the body of the while loop. It will end when 3^k > n, hence we will iterate log3(n) times, and each iteration the inner loop will resut in 3k-1 steps.
The above sum is equivalent to:
log3 n
---
\ k
-log3 n + / 3
---
k=0
The above is a geometric series [wiki], which is equal to: (1-3log3n)/(1-3), or simplified, it is equal to (nlog33-1)/2, and hence (n-1)/2.
The total number of steps is thus bounded by: (n-1)/2 - log3n, or formulated more simply O(n).
The body of the inner loop is going to be executed 1, 3, 9, 27, ..., 3^k times, where k = ceil(log3(n)).
Here we can use the fact that Σ0 <= i < k3i <= 3k. One can prove it by induction.
So we can say that the inner loop executes no more than 2*3^k times, where 3^k < 3n, which is linear in n, namely O(n).
First of all, you're really calculating the running time, but the number of time-consuming operations. Here, each call to printf_s is one.
Sometimes if you're not good at maths, you can still find the number with experimentation. The algorithm compiled with -O3 is quite fast to be tested with various n. I replaced printf_s with a simple increment to a counter that is then returned from the function, and use unsigned long long as the type. With those changes we get
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <inttypes.h>
unsigned long long alg(unsigned long long n) {
unsigned long long rv = 0;
unsigned long long i = 1;
unsigned long long j;
while (i <= n) {
for (j = 1; j < i; j++)
rv += 1;
i *= 3;
}
return rv;
}
int main(void) {
unsigned long long n = 1;
for (n = 1; n <= ULONG_MAX / 10; n *= 10) {
unsigned long long res = alg(n);
printf("%llu %llu %f\n", n, res, res/(double)n);
}
}
the program runs in 0.01 seconds because GCC is clever enough to completely eliminate the inner loop. The output is
1 0 0.000000
10 10 1.000000
100 116 1.160000
1000 1086 1.086000
10000 9832 0.983200
100000 88562 0.885620
1000000 797148 0.797148
10000000 7174438 0.717444
100000000 64570064 0.645701
1000000000 581130714 0.581131
10000000000 5230176580 0.523018
100000000000 141214768216 1.412148
1000000000000 1270932914138 1.270933
10000000000000 11438396227452 1.143840
100000000000000 102945566047294 1.029456
1000000000000000 926510094425888 0.926510
10000000000000000 8338590849833250 0.833859
100000000000000000 75047317648499524 0.750473
1000000000000000000 675425858836496006 0.675426
And from that we can see that the ratio of number of prints to n is not really converging, but it seems to be very much bounded by constants on both sides, thus O(n).
I am making a library management in C for practice. Now, in studentEntry I need to generate a long int studentID in which every digit is non-zero. So, I am using this function:
long int generateStudentID(){
srand(time(NULL));
long int n = 0;
do
{
n = rand() % 10;
}while(n == 0);
int i;
for(i = 1; i < 10; i++)
{
n *= 10;
n += rand() % 10;
}
if(n < 0)
n = n * (-1); //StudentID will be positive
return n;
}
output
Name : khushit
phone No. : 987546321
active : 1
login : 0
StudentID : 2038393052
Wanted to add another student?(y/n)
I wanted to remove all zeros from it. Moreover, when I run the program the first time the random number will be the same as above, and second time random number is same as past runs like e.g:-
program run 1
StudentID : 2038393052
StudentID : 3436731238
program run 2
StudentID : 2038393052
StudentID : 3436731238
What do I need to fix these problems?
You can either do as gchen suggested and run a small loop that continues until the result is not zero (just like you did for the first digit) or accept a small bias and use rand() % 9 + 1.
The problem with the similar sequences has its reason with the coarse resolution of time(). If you run the second call of the function to fast after the first you get the same seed. You might read this description as proposed by user3386109 in the comments.
A nine-digit student ID with no zeros in the number can be generated by:
long generateStudentID(void)
{
long n = 0;
for (int i = 0; i < 9; i++)
n = n * 10 + (rand() % 9) + 1;
return n;
}
This generates a random digit between 1 and 9 by generating a digit between 0 and 8 with (rand() % 9) and adding 1. There's no need to for loops to avoid zeros.
Note that this does not call srand() — you should only call srand() once in a given program (under normal circumstances). Since a long must be at least 32 bits and a 9-digit number only requires 30 bits, there cannot be overflow to worry about.
It's possible to argue that the result is slightly biassed in favour of smaller digits. You could use a function call to eliminate that bias:
int unbiassed_random_int(int max)
{
int limit = RAND_MAX - RAND_MAX % max;
int value;
while ((value = rand()) >= limit)
;
return value % max;
}
If RAND_MAX is 32767 and max is 9, RAND_MAX % 9 is 7. If you don't ignore the values from 32760 upwards, you are more likely to get a digit in the range 0..7 than you are to get an 8 — there are 3642 ways to each of 0..7 and only 3641 ways to get 8. The difference is not large; it is smaller if RAND_MAX is bigger. For the purposes on hand, such refinement is not necessary.
Slightly modify the order of your original function should perform the trick. Instead of removing 0s, just do not add 0s.
long int generateStudentID(){
srand(time(NULL));
long int n = 0;
for(int i = 0; i < 10; i++)
{
long int m = 0;
do
{
m = rand() % 10;
}while(m == 0);
n *= 10;
n += m;
}
//Not needed as n won't be negative
//if(n < 0)
//n = n * (-1); //StudentID will be positive
return n;
}
I'm writing a code in C that returns the number of times a positive integer can be expressed as sums of perfect squares of two positive integers.
R(n) is the number of couples (x,y) such that x² + y² = n where x, y, n are all
non negative integers.
To compute R(n), I need to first find the prime factorization of n.
The problem is that I've tried a lot of algorithm for prime factorization that I can use on C but I need my code to be as fast as possible, so I would appreciate it if anyone can give me what he/she considers as the fastest algorithm to compute the prime factorization of a number as large as 2147483742.
What an odd limit; 2147483742 = 2^31 + 94.
As others have pointed out, for a number this small trial division by primes is most likely fast enough. Only if it isn't, you could try Pollard's rho method:
/* WARNING! UNTESTED CODE! */
long rho(n, c) {
long t = 2;
long h = 2;
long d = 1;
while (d == 1) {
t = (t*t + c) % n;
h = (h*h + c) % n;
h = (h*h + c) % n;
d = gcd(t-h, n); }
if (d == n)
return rho(n, c+1);
return d;
}
Called as rho(n,1), this function returns a (possibly-composite) factor of n; put it in a loop and call it repeatedly if you want to find all the factors of n. You'll also need a primality checker; for your limit, a Rabin-Miller test with bases 2, 7 and 61 is proven accurate and reasonably fast. You can read more about programming with prime numbers at my blog.
But in any case, given such a small limit I think you are better off using trial division by primes. Anything else might be asymptotically faster but practically slower.
EDIT: This answer has received several recent upvotes, so I'm adding a simple program that does wheel factorization with a 2,3,5-wheel. Called as wheel(n), this program prints the factors of n in increasing order.
long wheel(long n) {
long ws[] = {1,2,2,4,2,4,2,4,6,2,6};
long f = 2; int w = 0;
while (f * f <= n) {
if (n % f == 0) {
printf("%ld\n", f);
n /= f;
} else {
f += ws[w];
w = (w == 10) ? 3 : (w+1);
}
}
printf("%ld\n", n);
return 0;
}
I discuss wheel factorization at my blog; the explanation is lengthy, so I won't repeat it here. For integers that fit in a long, it is unlikely that you will be able to significantly better the wheel function given above.
There's a fast way to cut down the number of candidates. This routine tries 2, then 3, then all the odd numbers not divisible by 3.
long mediumFactor(n)
{
if ((n % 2) == 0) return 2;
if ((n % 3) == 0) return 3;
try = 5;
inc = 2;
lim = sqrt(n);
while (try <= lim)
{
if ((n % try) == 0) return try;
try += inc;
inc = 6 - inc; // flip from 2 -> 4 -> 2
}
return 1; // n is prime
}
The alternation of inc between 2 and 4 is carefully aligned so that it skips all even numbers and numbers divisible by 3. For this case: 5 (+2) 7 (+4) 11 (+2) 13 (+4) 17
Trials stop at sqrt(n) because at least one factor must be at or below the square root. (If both factors were > sqrt(n) then the product of the factors would be greater than n.)
Number of tries is sqrt(m)/3, where m is the highest possible number in your series. For a limit of 2147483647, that yields a maximum of 15,448 divisions worst case (for a prime near 2147483647) including the 2 and 3 tests.
If the number is composite, total number of divisions is usually much less and will very rarely be more; even taking into account calling the routine repeatedly to get all the factors.
I wrote two algos to get the sum of the proper divisors of a given number,to find perfect number or abundant number.
long sum_divisors_1(int a)
{
int i, t;
long sum = 1;
for (i = 2, t = sqrt(a); i < t + 1; i++) {
if (a % i == 0) {
sum += i;
sum += a / i;
}
}
if (a % t == 0)
sum -= t;
return sum;
}
long sum_divisors_2(int a)
{
int i, sum;
sum = 0;
for (i = 1; i < (int) (a / 2 + 1); i++) {
if (a % i == 0)
sum += i;
}
return sum;
}
And I think they are both correct and the first one is faster. But I can only get the correct result from the second algo. Other parts of the code are the same.
Any suggestions? And how the proper divisors are found in real industrial programming?
Thanks in advance.
Your problem lies here:
if (a % t == 0)
sum -= t;
Since you're casting t to an int from a floating point, it will round down to an integer value. This also assumes that t is the actual square root when it isn't. This will evaluate to true when a number has factors x & x+1 (the unit test I posted as well fails when i = 6 because it's square root is 2.45 and 2 is a factor).
The check really should be:
if (t*t == a)
sum -= t;
This is an old question but I was browsing.
There's a much faster algorithm to find the sum of proper divisors.
Find the prime factors of a number using Sieve of Eratosthenes (or Atkin). With wheel factorisation the first 1m prime numbers will take maybe 30ms.
Then the sum of all divisors is
For each n
sum += (n ^ (m+1) - 1) / (n-1)
where n is the factor, m is the power of that factor.
Eg for 220
2^2 5 11 are the factors
So it's sum of
2 ^ (2+1) - 1 / 1 *
5 ^ (1+1) - 1 / 4 *
11 ^ (1+1) - 1 / 10
= 7 * 6 * 12
= 504
This is the sum of ALL divisors, so just subtract N
504-220 = 284
This should be a lot faster than trying all the numbers, especially if you precalculate the sieve and reuse it.
Here's a simple unit test I wrote in C# that will quickly invalidate #1 given #2 is correct:
for(int i = 4; i < 28124; i++)
{
Assert.AreEqual(sum_divisors_2(i), sum_divisors_1(i), "Failed when i = {0}", i);
}
Too big for a comment...
Templatetypedef solved your problem; however the fastest possible way to compute the prime factors is to precompute all the prime factors up to sqrt(MAX_INT) with Eratostene sieve, store it into an array and then use it to factorize the number a. This is really really really much faster.