Algorithm complexity with iterations. Is it logarithmic or exponential? - c

If I have the following algorithm
for (i = 1; i <= 4 * n; i = i * 4) {
for (k = 1; k < 1000; k = 2 * k) {
print(k);
}
print(i);
}
how can I calculate its complexity?
I only understand that for one iteration of for(i=1; i≤4n; i=i*4), the line with print(i) is O(1), and for one iteration of for(k=1; k<1000; k=2*k), the line with print(k) is O(1).
I'm not sure how to proceed.

Here's the inner loop:
for(k=1; k<1000; k=2*k) {
print(k);
}
That loop is constant time, because there are no free variables. It's always going to call print exactly 9 times, for k ∈ {1,2,4,8,16,32,64,128,256,512}.
The outer loop is O(log n), because it will execute ⌊log₄ 4n⌋ times.
Overall, the program fragment you posted (if we add the final closing brace you omitted) is O(log n).

Related

What is the runtime of the inner loop?

I made a function that loops through the array and prints any two values of the array that can add up to a value K. The outer for loop is O(n), but the inner loop is a bit confusing to me if the runtime is a O(Log n) or O(n). can you help please? Thank you!!
int canMakeSum(int *array, int n, int key){
int i, j;
for(i = 0; i < n; i++){
for(j = (i+1); j < n; j++){
if(array[i]+array[j] == key){
printf("%d + %d = %d\n", array[i], array[j], key);
}
}
}
}
As others have already shown, the inner loop is still O(n); it's a mean of n/2 iterations, the values 1 through n distributed evenly over the iterations of the outer loop.
Yes, you can solve the problem in O(n log n).
First, sort the array; this is n log n. Now, you have a linear (O(n)) process to find all combinations.
lo = 0
hi = n-1
while lo < hi {
sum = array[lo] + array[hi]
if sum == k {
print "Success", array[lo], array[hi]
lo += 1
hi -= 1
}
else if sum < k // need to increase total
lo += 1
else // need to decrease total
hi -= 1
As the inner loops is dependent to the value of the outer loop, you can't find the complexity of the total porgram without analyzing the both with together. The complexity of the inner loop is n - i - 1.
If you want to compute the complexity of the program, you can sum over n - i -1 from i = 0 to i = n - 1. Hence, the total complexity is T(n) = (n - 1) + (n-2) + ... + 1 + 0 = (n-1)n/2 = \Theta(n^2) (as the statment in the inner loop has a constant complexity (\Theta(1))).
Although the inner loop decreases in the number of items it scans for each iteration in the outer loop, it would still be O(n). The overall time complexity is O(n^2).
Imagine you've an array of 25000 elements. At the starting point at i = 0 and j = 1, the number of elements that j will iterate through (worst case no matches to key) is 24999 elements. Which is a small difference from the total number of elements, so it is 'like' going through n elements.

time complexity of specific nested loop in c

Why is the time complexity of this function is T(n) = O(n) and not T(n) = O(n*log(n))? I think the answer to this question lies inside the first loop. isn't the time complexity of the first loop is log(n)?
void f(int n) {
for( ; n>0; n/=2 ){
int i;
for (i=0; i<n; i++)
printf("Hey");
}
}
Consider that the first loop changes the value of n. It divides n by 2 in each iteration which show that first loop runs log(n) step. We can rewrite it in this way: for (int k = 1; k <= n; k *= 2). Second loop runs upto k. So we have

Extracting Upper Bound from this Loop with Multiplication Increment

I understand how to use summation to extract the time complexity (and Big O) from linear for-loops using summation, but how would you use it for multiplication incremental loops to get O(logn). For example, the code below is O(nlogn), but I don't know why.
for (i = 0; i < n; i++)
for (j = 1; j < n; j*7)
/*some O(1) operations*/
Also, why is a while loop O(logn) and a do-while loop O(n^2).
At each iteration of the inner loop you perform j = j * 7 (I assume this is what you meant)
That is, at each iteration j = 7j
After n iterations, j = j*7*7*7*7*...*7*7 = j*(7 ^ n)
Let n be the number we want to reach and m the number of iterations, so:
n = j*7*7*7*...7 = j*(7 ^ m)
Let's take a log from both sides:
log(n) = log(j * (7 ^ m)) ~= m*log(7) = O(m)
So, as we can see - the inner loop runs O(log(n)) times.

What is the complexity of this sum algorithm?

#include <stdio.h>
int main() {
int N = 8; /* for example */
int sum = 0;
for (int i = 1; i <= N; i++)
for (int j = 1; j <= i*i; j++)
sum++;
printf("Sum = %d\n", sum);
return 0;
}
for each n value (i variable), j values will be n^2. So the complexity will be n . n^2 = n^3. Is that correct?
If problem becomes:
#include <stdio.h>
int main() {
int N = 8; /* for example */
int sum = 0;
for (int i = 1; i <= N; i++)
for (int j = 1; j <= i*i; j++)
for (int k = 1; k <= j*j; k++)
sum++;
printf("Sum = %d\n", sum);
return 0;
}
Then you use existing n^3 . n^2 = n^5 ? Is that correct?
We have i and j < i*i and k < j*j which is x^1 * x^2 * (x^2)^2 = x^3 * x^4 = x^7 by my count.
In particular, since 1 < i < N we have O(N) for the i loop. Since 1 < j <= i^2 <= N^2 we have O(n^2) for the second loop. Extending the logic, we have 1 < k <= j^2 <= (i^2)^2 <= N^4 for the third loop.
Inner to Outer loops, we execute up to N^4 times for each j loop, and up to N^2 times for each i loop, and up to N times over the i loop, making the total be of order N^4 * N^2 * N = N^7 = O(N^7).
I think the complexity is actually O(n^7).
The first loop executes N steps.
The second loop executes N^2 steps.
In the third loop, j*j can reach N^4, so it has O(N^4) complexity.
Overall, N * N^2 * N^4 = O(N^7)
For i = 1 inner loop runs 1^1 times, for i = 2inner loop runs 2^2 times .... and for i = N inner loop runs N^N times. Its complexity is (1^1 + 2^2 + 3^3 + ...... + N^N) of order O(N^3).
In second case, for i = N first inner loop iterates N^N times and hence the second inner loop(inner most) will iterate up to N * (N^N) * (N^N) times. Hence the complexity is of order N * N^2 * N^4, i.e, O(N^7).
Yes. In the first example, the i loop runs N times, and the inner j loop tuns i*i times, which is O(N^2). So the whole thing is O(N^3).
In the second example there is an additional O(N^4) loop (loop to j*j), so it is O(N^5) overall.
For a more formal proof, work out how many times sum++ is executed in terms of N, and look at the highest polynomial order of N. In the first example it will be a(N^3)+b(N^2)+c(N)+d (for some values of a, b, c and d), so the answer is 3.
NB: Edited re example 2 to say it's O(N^4): misread i*i for j*j.
Consider the number of times all loops will be called.
int main() {
int N = 8; /* for example */
int sum = 0;
for (int i = 1; i <= N; i++) /* Called N times */
for (int j = 1; j <= i*i; j++) /* Called N*N times for i=0..N times */
for (int k = 1; k <= j*j; k++) /* Called N^2*N^2 times for j=0..N^2 times and i=0..N times */
sum++;
printf("Sum = %d\n", sum);
return 0;
}
Thus sum++ statement is called O(N^4)*O(N^2)*O(N) times = O(N^7) and this the overall complexity of the program.
The incorrect way to solve this (although common, and often gives the correct answer) is to approximate the average number of iterations of an inner loop with its worst-case. Here, the inner loop loops at worst O(N^4), the middle loop loops at worst O(N^2) times and the outer loop loops O(N) times, giving the (by chance correct) solution of O(N^7) by multiplying these together.
The right way is to work from the inside out, being careful to be explicit about what's being approximated.
The total number of iterations, T, of the increment instruction is the same as your code. Just writing it out:
T = sum(i=1..N)sum(j=1..i^2)sum(k=1..j^2)1.
The innermost sum is just j^2, giving:
T = sum(i=1..N)sum(j=1..i^2)j^2
The sum indexed by j is a sum of squares of consecutive integers. We can calculate that exactly: sum(j=1..n)j^2 is n*(n+1)*(2n+1)/6. Setting n=i^2, we get
T = sum(i=1..N)i^2*(i^2+1)*(2i^2+1)/6
We could continue to compute the exact answer, by using the formula for sums of 6th, 4th and 2nd powers of consecutive integers, but it's a pain, and for complexity we only care about the highest power of i. So we can approximate.
T = sum(i=1..N)(i^6/3 + o(i^5))
We can now use that sum(i=1..N)i^p = Theta(N^{p+1}) to get the final result:
T = Theta(N^7)

complexity for nested loops

I am trying to figure out the complexity of a for loop using Big O notation. I have done this before in my other classes, but this one is more rigorous than the others because it is on the actual algorithm. The code is as follows:
for(i=n ; i>1 ; i/=2) //for any size n
{
for(j = 1; j < i; j++)
{
x+=a
}
}
and
for(i=1 ; i<=n;i++,x=1) //for any size n
{
for(j = 1; j <= i; j++)
{
for(k = 1; k <= j; x+=a,k*=a)
{
}
}
}
I have arrived that the first loop is of O(n) complexity because it is going through the list n times. As for the second loop I am a little lost!
Thank you for the help in the analysis. Each loop is in its own space, they are not together.
Consider the first code fragment,
for(i=n ; i>1 ; i/=2) //for any size n
{
for(j = 1; j < i; j++)
{
x+=a
}
}
The instruction x+=a is executed for a total of n + n/2 + n/4 + ... + 1 times.
Sum of the first log2n terms of a G.P. with starting term n and common ratio 1/2 is, (n (1-(1/2)log2n))/(1/2). Thus the complexity of the first code fragment is O(n).
Now consider the second code fragment,
for(i=1 ; i<=n; i++,x=1)
{
for(j = 1; j <= i; j++)
{
for(k = 1; k <= j; x+=a,k*=a)
{
}
}
}
The two outer loops together call the innermost loop a total of n(n+1)/2 times. The innermost loop is executed at most log<sub>a</sub>n times. Thus the total time complexity of the second code fragment is O(n2logan).
You may formally proceed like the following:
Fragment 1:
Fragment 2 (Pochhammer, G-Function, and Stirling's Approximation):
With log(G(n)).
[UPDATE of Fragment 2]:
With some enhancements from "DISCRETE LOOPS AND WORST CASE PERFORMANCE" publication, by Dr. Johann Blieberger (All cases verified for a = 2):
Where:
Therefore,
EDIT: I agree the first code block is O( n )
You decrement the outer loop i by diving by 2, and in the inner loop you run i times, so the number of iterations will be a sum over all the powers of two less than or equal to N but greater than 0, which is nlog(n)+1 - 1, so O(n).
The second code block is O(loga(n)n2) assuming a is a constant.
The two outermost loops equate to a sum of all the numbers less than or equal to n, which is n(n-1)/2, so O(n2). Finally the inner loop is the powers of a less than an upper bound of n, which is O(logan).

Resources