What is the Big-O of this nested loop? - time-complexity

int z=1;
for(int i=0;i*i<n;i++){
z*=3;
for(int j=0;j<z;j++){
// Some code
}
}
Answer is O(3^n).
Is it correct? How to figure out time complexity of nested loop?

outer loop: i goes from 1 to sqrt(n);
inner loop: j,z goes up to 3^(sqrt(n));
"some code" will run 1 + 3 + 3^2 + ... + 3^(sqrt(n)) times
let sum = 1 + 3 + 3^2 + ... + 3^(sqrt(n))
sum - 3*sum = 1 - 3(sqrt(n) + 1)
sum = 1 - 3(sqrt(n) + 1) / (1-3) = 2( 3^(sqrt(n)+1) - 1 )
2( 3^(sqrt(n)+1) - 1 ) << O( 3^sqrt(n) )
O(3^sqrt(n)) is more accurate

You could approach the problem using Sigma notation this way:

Related

Theoretical time complexity calculation of nested dependent for loops [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
How do I calculate the big-O time complexity of the following nested for loop with dependent indices:
void function1 (int n)
{
int x = 0;
for (int i = 0; i <= n/2; i+=3)
for (int j = i; j <= n/4; j+=2)
x++;
}
Complexity of the code is defined as how many times your code will be executed for a given n.
There are two ways to do it.
Simulation: Run the code for different value of n and find out the values. In this case this is equivalent to the final value of x.
Theoretical:
Let's first check for each i how many times your code runs:
Using the arithmetic progression formula (a_n = a_1 + (k-1)*d):
i=0 => n/4 = 0 + (k-1)*2 => n/8 + 1 times
i=3 => n/4 = 3 + (k-1)*2 => (n-12)/8 + 1 times
i=6 => n/4 = 6 + (k-1)*2 => (n-24)/8 + 1 times
i=9 => n/4 = 9 + (k-1)*2 => (n-36)/8 + 1 times
Let's check the last i's now:
i=n/4 => n/4 = n/4 + (k-1)*2 => 1 times
i=n/4 - 3 => n/4 = (n/4-3) + (k-1)*2 => 3/2 + 1 times
i=n/4 - 6 => n/4 = (n/4-6) + (k-1)*2 => 6/2 + 1 times
So total number of times inner loop will be running is:
= (1) + (3/2 + 1) + (6/2 + 1) + (9/2 + 1) ... + ((n-12)/8 + 1)+ (n/8 + 1)
=> (0/2 + 1) + (3/2 + 1) + (6/2 + 1) + (9/2 + 1) ... + ((n-12)/8 + 1)+ (n/8 + 1)
Can be written as:
=> (0/2 + 3/2 + 6/2 + ... (n-12)/8 + n/8) + (1 + 1 + 1 ... 1 + 1)
Let's assume there are total P terms in the series:
Let's find out P:
n/8 = (0/2) + (P-1)*(3/2) => P = (n+12)/12
Now summing up the above series:
= [(P/2) (0/2 + (P-1) * 3/2)] + [P]
= P(3P+1)/4
= (n+12)(3(n+12)+12)/(4*12*12)
= (n^2 + 28n + 96)/192
So the final complexity of the code is
= (number of operation in each iteration) * (n^2 + 28n + 96)/192
Now look at the term (n^2 + 28n + 96)/192 For a very large n this will be close to ~n^2
Following is the complexity comparison:
Linear scale was difficult to analyse to I plotted log scale. Though for small n you don't see the complexity converging to n^2.
Using a very relax approach one can say that:
for (int i = 0; i <= n/2; i+=3){
for (int j = i; j <= n/4; j+=2) {
x++;
}
}
in the same as :
for (int i = 0; i <= n/4; i+=3){
for (int j = i; j <= n/4; j+=2) {
x++;
}
}
since with i > n/4 the inner loop will not execute. Moreover, to simplify the math you can say that the code is approximately the same as:
for (int i = 0; i < n/4; i+=3){
for (int j = i; j < n/4; j+=2) {
x++;
}
}
since the context is big-O it does not make a difference for the calculation of the upper-bound of the double loop. The number of iterations of a loop of the form:
for (int j = a; j < b; j += c)
can be calculated using the formula (b - a) /c. Hence, the inner loop will run approximately ((n/4) - i) / 2) times, or n/8 - i/2 times.
The outer-loop can be thought as running from k=0 until n/12. So with both loops we have
the summation of [k=0 to n/12] of (n/8 - 3k/2),
which is equivalent to
the summation [k=0 to n/12] of n/8 - the summation [k=0 to n/12] of 3k/2.
Hence,
(N^2) / 96 - the summation [[k=0 to n/12] of 3k/2
which is approximately (n^2) / 192. Therefore, the upper bound is O (n^2).

TIme complexity for a specific loop

What is the time complexity and tilde for the loop below?
for (int i = N/2; i < N; i++) {
for (int j = i; j < N; j++) {
doSomething(i, j);
}
}
I think that it runs N/2 + (N/2 + 1) + (N/2 + 2) + ... + (N-1) times, but how do I get it's time complexity and tilde?
For example - if N = 100, the loop will run 50 + 51 + 52 + 53 + ... + 99 times.
I am assuming doSomething(i, j); is not iterating all the elements between i and j; if this is the case, the complexity of this algorithm is O(N^2).
The outer loop for (int i = N/2; i < N; i++) { will execute O(N) times, cause N/2 is actually constant value.
The inner loop in worst case will execute N times (or N - i times) too, this will also merge with previous O(N).
Therefore, overall time complexity will be O(N^2) in worst case scenario.
The inner loop is executed:
N/2-1 times for i = N/2,
N/2-2 times for i = N/2+1
....
1 time for i = N-2
therefore the total time for the inner loop is :
(N/2-1) + (N/2-2) + .... (N/2-k) where k = N/2 - 1
= N/2*k - (1 + 2 + ... + k)
= N/2*(N/2-1) - (N/2-1)(N/2)/2
= N/2(N/2 - 1 - N/4 + 1/2)
= N/2(N/4 - 1/2)
= N^2/8 - N/4
Hence the order of growth of the code is of N^2
If you consider tilde notation which is defined as :
"∼g(n) to represent any quantity that, when divided by f(n), approaches 1 as n grows" from here, you can see that ~g(n) = ~N^2/8 because as N grows (N^2/8)/(N^2/8-N/4) approaches 1.

What is the time complexity of this nested loop?

I stumbled upon a loop for which I am not sure what the time complexity is. It is the following loop:
for(i = 1; i <= n^2; i++){
for(j = 1; j <= i; j++) {
//some elementary operation
}
}
I would have argued that the outer for-loop runs in n^2 and the inner for loop would also run in n^2 as for every iteration of the outer-loop we do n^2 - (n^2 - 1), n^2 - (n^2 - 2),..., n^2. Am I totally going in the wrong direction here?
So the time complexity would be in n^4
The number of operation will be :
1 + 2 + 3 + 4 + 5 + ... + n²
Which is equal to (n² * (n² - 1)) / 2.
The Big O notation is O(n^4). You are correct.
It's a simple arithmetic progression happening here.
Every new iteration is bigger by 1.
Outer loop will do n^2 operations which will result for following sequence:
1 + 2 + 3 + ... + n + ... + n^2 = n^2 (n^2+1) / 2 = O(n^4)

Solve: T(n) = T(n/2) + n/2 + 1

I struggle to define the running time for the following algorithm in O notation. My first guess was O(n), but the gap between the iterations and the number I apply isn't steady. How have I incorrectly defined this?
public int function (int n )
{
if ( n == 0) {
return 0;
}
int i = 1;
int j = n ;
while ( i < j )
{
i = i + 1;
j = j - 1;
}
return function ( i - 1) + 1;
}
The while is executed in about n/2 time.
The recursion is executed passing as n a value that is about half of the original n, so:
n/2 (first iteration)
n/4 (second iteration, equal to (n/2)/2)
n/8
n/16
n/32
...
This is similar to a geometric serie.
Infact it can be represented as
n * (1/2 + 1/4 + 1/8 + 1/16 + ...)
So it converges to n * 1 = n
So the O notation is O(n)
Another approach is to write it down as T(n) = T(n/2) + n/2 + 1.
The while loop does n/2 work. Argument passed to next call is n/2.
Solving this using the master theorem where:
a = 1
b = 2
f = n/2 + 1
Let c=0.9
1*(f(n/2) + 1) <? c*f(n)
1*(n/4)+1 <? 0.9*(n/2 + 1)
0.25n + 1 <? 0.45n + 0.9
0 < 0.2n - 0.1
Which is:
T(n) = Θ(n)

why is the time complexity of bubble sort's best case being O(n)

I deduced the time complexity of bubble sort in its best case according to the mothod used in book ALGORITHMS 2.2. But the answer turned out to be O(n^2).
Here's my derivation, hope anyone can help me find out where is wrong:
public void bubbleSort(int arr[]) {
for(int i = 0, len = arr.length; i < len - 1; i++) {
for(int j = 0; j < len - i - 1; j++) {
if(arr[j + 1] < arr[j])
swap(arr, j, j + 1);
}
}
}
Statements cost times
i = 0,len = arr.length c1 1
i < len - 1 c2 n
i++ c3 n - 1
j = 0 c4 n - 1
j < len - i - 1 c5 t1(i=0) + t1(i=1) + ... + t1(i = n-2)
j++ c6 t2(i=0) + t2(i=1) + ... + t2(i = n-2)
arr[j + 1] < arr[j] c7 t3(i=0) + t3(i=1) + ... + t3(i = n-2)
swap(arr, j, j + 1) c8 t4(i=0) + t4(i=1) + ... + t4(i = n-2)
T(n) = c1 + c2n + c3(n - 1) + c4(n - 1) + c5t5 + c6t6 + c7t7 + c8t8
= c1 + c2n + c3(n - 1) + c4(n - 1) + c5[t1(i=0) + t1(i=1) + ... + t1(i = n-2)] + c6[t2(i=0) + t2(i=1) + ... + t2(i = n-2)] + c7[t3(i=0) + t3(i=1) + ... + t3(i = n-2)] + c8[t4(i=0) + t4(i=1) + ... + t4(i = n-2)];
in its best cast, the sequence is already positive before sorting. Then t8 sould be 0.
T(n) = c1 + c2n + c3(n - 1) + c4(n - 1) + c5[t1(i=0) + t1(i=1) + ... + t1(i = n-2)] + c6[t2(i=0) + t2(i=1) + ... + t2(i = n-2)] + c7[t3(i=0) + t3(i=1) + ... + t3(i = n-2)]
The time complexity is O(n^2)
Your implementation
public void bubbleSort(int arr[]) {
for(int i = 0, len = arr.length; i < len - 1; i++) {
for(int j = 0; j < len - i - 1; j++) {
if(arr[j + 1] < arr[j])
swap(arr, j, j + 1);
}
}
}
lacks the control whether there was any swap in the inner loop, and the breaking out of the outer loop if there wasn't.
That control makes it possible that the best case (an already sorted array) is O(n), since then there are no swaps in the inner loop when it runs the first time.
public void bubbleSort(int arr[]) {
boolean swapped = true;
for(int i = 0, len = arr.length; swapped && i < len - 1; i++) {
swapped = false;
for(int j = 0; j < len - i - 1; j++) {
if(arr[j + 1] < arr[j]) {
swap(arr, j, j + 1);
swapped = true;
}
}
}
}
The best case for bubble sort is when the elements are already sorted.
The usual implementation gives O(n^2) time complexity for best, average, worst case.
We can modify the bubble sort by checking if array is sorted or not(a swap would indicate an unsorted array) at every iteration.
As soon as the array is found to be sorted(if no swap occurs) control exits from loops or loop continues to execute till length-1.
And same is true for insertion sort as well!
I am not sure what are you counting. In general, when you are talking about comparison sort algorithms you should count the number of comparisons made. Bubble sort is regarded as such. In this case the algorithm you presented is O(n^2).
If you count the number of swaps its O(1) or maybe even one could say O(0). It is however rare to analyze Bubble sort like that.
You can, however very easily improve Bubble to get O(N) on best case. E.g by introducing a flag swap_was_made. If its false at the end of inner for you can finish. On best case it will cut complexity to O(N) (one inner for loop). In case of fair even distribution it cuts the expected or average complexity to O(N^2/2) ... But please double check me on it I might be wrong. Didn't do the math here.