time complexity of nested for loop with j<=i condition - time-complexity

I had this question on a assignment.
Determine the time-complexity of the nested loop
for(int i=1; i<=n; i=2*i){
for(int j=1; j<=i; i=2*j){
stuff
}
}
I understand that with i and j being incremented by 2x that the complexity would be something along the lines of log2(n) * log2(n), but with the inner loop running to i rather than n I'm completely lost
I need to know the complexity of the nested loop and a step-by-step on how it was solved.

The inner loop runs log(i) + 1 times (log base 2).
Adding the outer-loop, sum the above for i = 1, 2, 4, ... n.
So: (log(1) + 1) + (log(2) + 1) + (log(4) + 1) + ... + (log(n) + 1)
which is: 1 + 2 + 3 + ... + log(n)
using the sum of arithmetic series is: (log(n) + 1) * (log(n) + 2) / 2 = (log(n)*log(n) + 3log(n) + 2) / 2 = O(log(n) * log(n))

Let's assume n=16 for example, so i will have values i = 1, 2, 4, 8, 16.
So: i is basically taking value as log(n) i.e log(16) i.e five iterations.
now for the value of j, it's taking value as log(1) + log(2) + log(4) + log(8) + log(16). It's basically equal to log(i) in each iteration.
So combining the values we got from the above two statements, we can say the time-complexity of the above code is O(log(n) * log(i)).
This is my understanding about the code.

Related

determine the time complexity in Java

public class complexity {
public int calc(int n) {
int a = 2 * Math.abs(n);
int b = Math.abs(n) - a;
int result = n;
for (int i = 0; i < a; i++) {
result += i;
for (int j = 0; j > b; j--) {
result -= j;
}
for (int j = a/2; j > 1 ; j/=2) {
System.out.println(j);
}
}
int tmp = result;
while (tmp > 0) {
result += tmp;
tmp--;
}
return result;
}
}
I have been given the following program in Java and need to determine the time complexity (Tw(n)).
I checked those site:
Big O, how do you calculate/approximate it?
How to find time complexity of an algorithm
But I have still problem too understand it.
Here is the given solution by the professor.From the for loop on I didn't understand anything how he came up with the different time complexity. Can anybody explain it ?
let's go over it step by step :
before the for loop every instruction is executed in a constant time
then:
for(int i = 0; i < a; i++) is executed 2n + 1 times as a = 2*n so 0=> a = 2n steps and the additional step is when i = 2n the program has to execute that step and when it finds that i = 2n it breaks out of the loop.
Now each instruction in the loop has to be executed 2n times (as we are looping from 0 to 2n - 1) which explains why result += i is done 2n times.
The next instruction is another for loop so we apply the same logic about the line
for (int j = 0; j > b; j--) : as b = -n this instruction will go from 0 down to -n plus the extra step I mentioned in the first for loop which means : 0 -> -n => n steps + 1 = n+1 steps and as this instruction is in the other loop it will be execited 2n times hence 2n * (n+1)
Instructions inside this for loop are done n times and therefore result -= j is done n times and as the loop itself is done 2n times (result -= j) will be done n*2n times
Same goes for the last for loop except here we are going from a/2 which is n as a = 2n to 1 and each time we are dividing by 2, this is a bit complicated so lets do some steps first iteration j = n then j = n/2 then j = n/4 till j is <= 1 so how many steps do we need to reach 1 ?
to reach n/2 we need 1 = log(2) step n => n/2
to reach n/4 we need 2 2 = log(4) steps n => n/2 => n/4
we remark here that to reach n/x we need log(x) steps and as 1 = n/n we need log(n) steps to reach 1
therefore each instruction in this loop is executed log(n) times and as it is in the parent loop it has to be done 2n times => 2n*log(n) times.
for the while loop : result = n + (0 + 1 + 2 + .. + 2n) + (0 + 1 + .. + 2(n^2))
we did this in the for loop and then you do the arithmetic sequence sums it gives
result = n^2 (n+1) and here you go.
I hope this is clear don't hesitate to ask otherwise !
I am willing to clarify i.e. the last while loop. Unfortunately my answer does not match the professor's. And I am not that certain to have not made a grave mistake.
result starts with n
for i from 0 upto 2|n|
result += i
2|n| times done, average i.|n| so result increased by: ~2n.n = 2n².
for j from 0 downto -|n|
result -= j
2|n| times done, result increased by: 2n.n/2 = n²
So result is n + 3n²
The outer for loop remains O(n²) as the inner println-for has only O(log n).
The last while loop would be the same as:
for tmp from n + 3n² downto 0
result += tmp
This is also O(n²) like the outer for loop, so the entire complexity is O(n²).
The result is roughly 3n².3n²/2 or. 4.n4.

What is the time complexity of a recursive algorithm given by the recurrence T(n) = 2T(n-1) + 1

I am completely new to this so a step by stem would help greatly, thank you.
First, you can start with iteration method to understand how this behaves.
T(n) = 2T(n-1) + 1 =
= 2*(2T(n-2) + 1) + 1 = 4T(n-2) + 3
= 4(2T(n-3) + 1) + 3 = 8T(n-3) + 7
= 8*(2T(n-4) + 1) + 7 = 16T(n-4) + 15
= 16*(2T(n-5) + 1) + 15 = 32T(n-5) + 31
Now, that we understand the behavior, we can tell
T(n) = 2^i * T(n-i) + (2^i - 1)
Now, we need to use the base clause (which is not given in question), and extract for i=n. For example, if T(0) = 0:
T(n) = 2^n * T(0) + (2^n - 1) = 2^n - 1
This is in O(2^n), when calculating asymptotic complexity.
Note: Iteration method is good and easy to follow - but it is not a formal proof. To formally prove the complexity, you are going to need another tool, such as induction.

is log(n!) equivalent to nlogn? (Big O notation)

In my book there is a multiple choice question:
What is the big O notation of the following function:
n^log(2) +log(n^n) + nlog(n!)
I know that log(n!) belongs to O(nlogn), But I read online that they are equivalent. how is log(n!) the same thing as saying nlogn?
how is:
log(n!) = logn + log(n-1) + ... + log2 + log1
equivalent to nlogn?
Let n/2 be the quotient of the integer division of n by 2. We have:
log(n!) = log(n) + log(n-1) + ... + log(n/2) + log(n/2 - 1) + ... + log(2) + log(1)
>= log(n/2) + log(n/2) + ... + log(n/2) + log(n/2 - 1) + ... + log(2)
>= (n/2)log(n/2) + (n/2)log(2)
>= (n/2)(log(n) -log(2) + log(2))
= (n/2)log(n)
then
n log(n) <= 2log(n!) = O(log(n!))
and n log(n) = O(log(n!)). Conversely,
log(n!) <= log(n^n) = n log(n)
and log(n!) = O(n log(n)).

Solving recurrence relation T(n) = T(n-1) + n

I see from a previous answer to this question, the person gave:
T(n) = T(n-2) + n-1 + n
T(n) = T(n-3) + n-2 + n-1 + n
T(n) = T(n-k) +kn - k(k-1)/2
I'm not understanding completely the third line. I can see they may have derived it from arithmetic series formula summation of 1/2n(n+1)? But how did they get kn and the minus sign in front of k(k-1)/2?
starting from:
T(n) = T(n-2) + n-1 + n
we may rewrite it as follows:
T(n) = T(n-2) + 2n - 1
The second formula says:
T(n) = T(n-3)+n-2+n-1+n
let us convert it the same way we do with the first one:
T(n) = T(n-3)+n+n+n-2-1
T(n) = T(n-3)+3n-2-1
By expanding more terms, we notice that the number subtracted from n in the recursive term:T(n-3) is always the same as the number multiplied by n. we may rewrite it as follows:
T(n) = T(n-k)+kn+...
we also notice that -2 -1 is the arithmetic series but negated and stars from k-1. the arithmetic of k-1 is (k-1)*k/2 just like n(n+1)/2. so the relation would be
T(n) = T(n-k)+kn-(k-1)*k/2 or T(n) = T(n-k)+kn-k*(k-1)/2
Hope this help ;)
The k(k-1)/2 term is just the sum of the numbers 0 to k-1. You can see why you need to subtract it from the following calculation:
T(n) =
T(n-k) + n + (n-1) + (n-2) + ... + (n-(k-1)) =
T(n-k) + (n-0) + (n-1) + (n-2) + ... + (n-(k-1)) =
T(n-k) + n + n + n + ... + n - 0 - 1 - 2 ... - (k-1) =
T(n-k) + kn - (0 + 1 + 2 + ... + (k-1)) =
T(n-k) + kn - k*(k-1)/2
If you look closly:
T(n) = T(n-2) + n-1 + n = T(n-2) + 2n -1
T(n)= T(n-3) + n-2 + n-1 + n = T(n-3)+ 3n -(2+1)
.
.
.
T(n)= T(n-k) + n-(k-1) + n-(k-2) + ... + n = T(n-k) + K * n + (-1 -2 - ... -(k-2) -(k-1))= T(n-k) + kn - k(k-1)/2
You can use recurrence theorem to demonstrate it

Complexity of the program [duplicate]

In the book Programming Interviews Exposed it says that the complexity of the program below is O(N), but I don't understand how this is possible. Can someone explain why this is?
int var = 2;
for (int i = 0; i < N; i++) {
for (int j = i+1; j < N; j *= 2) {
var += var;
}
}
You need a bit of math to see that. The inner loop iterates Θ(1 + log [N/(i+1)]) times (the 1 + is necessary since for i >= N/2, [N/(i+1)] = 1 and the logarithm is 0, yet the loop iterates once). j takes the values (i+1)*2^k until it is at least as large as N, and
(i+1)*2^k >= N <=> 2^k >= N/(i+1) <=> k >= log_2 (N/(i+1))
using mathematical division. So the update j *= 2 is called ceiling(log_2 (N/(i+1))) times and the condition is checked 1 + ceiling(log_2 (N/(i+1))) times. Thus we can write the total work
N-1 N
∑ (1 + log (N/(i+1)) = N + N*log N - ∑ log j
i=0 j=1
= N + N*log N - log N!
Now, Stirling's formula tells us
log N! = N*log N - N + O(log N)
so we find the total work done is indeed O(N).
Outer loop runs n times. Now it all depends on the inner loop.
The inner loop now is the tricky one.
Lets follow:
i=0 --> j=1 ---> log(n) iterations
...
...
i=(n/2)-1 --> j=n/2 ---> 1 iteration.
i=(n/2) --> j=(n/2)+1 --->1 iteration.
i > (n/2) ---> 1 iteration
(n/2)-1 >= i > (n/4) ---> 2 iterations
(n/4) >= i > (n/8) ---> 3 iterations
(n/8) >= i > (n/16) ---> 4 iterations
(n/16) >= i > (n/32) ---> 5 iterations
(n/2)*1 + (n/4)*2 + (n/8)*3 + (n/16)*4 + ... + [n/(2^i)]*i
N-1
n*∑ [i/(2^i)] =< 2*n
i=0
--> O(n)
#Daniel Fischer's answer is correct.
I would like to add the fact that this algorithm's exact running time is as follows:
Which means: