nested for loop time complexity with function calls - algorithm

for(i = 1; i < a; i++){
for(j = 1; j < b; j = j + 3){
if((i+j) % 2 == 0)
Func()
}
}
In this case, I thought it is O(a*b) and Theta(a*b).
Did I analyze the Complexity correctly?

First of all, you, probably, mean
if ((i + j) % 2 == 0)
instead of
if (i + j % 2 == 0)
since when i is positive, j % 2 non-negative then i + j % 2 is positive and thus i + j % 2 never equals to zero: Func() doesn't run at all.
Your answer is correct one: the complexity is
a * // from the first loop
b / 3 * // from the second loop
1 // from the condition (it always true)
So you have
Θ(a * b / 3 * 1) = Θ(ab)

Related

How to find the time complexity of these two programs? [duplicate]

int sum = 0;
for(int i = 1; i < n; i++) {
for(int j = 1; j < i * i; j++) {
if(j % i == 0) {
for(int k = 0; k < j; k++) {
sum++;
}
}
}
}
I don't understand how when j = i, 2i, 3i... the last for loop runs n times. I guess I just don't understand how we came to that conclusion based on the if statement.
Edit: I know how to compute the complexity for all the loops except for why the last loop executes i times based on the mod operator... I just don't see how it's i. Basically, why can't j % i go up to i * i rather than i?
Let's label the loops A, B and C:
int sum = 0;
// loop A
for(int i = 1; i < n; i++) {
// loop B
for(int j = 1; j < i * i; j++) {
if(j % i == 0) {
// loop C
for(int k = 0; k < j; k++) {
sum++;
}
}
}
}
Loop A iterates O(n) times.
Loop B iterates O(i2) times per iteration of A. For each of these iterations:
j % i == 0 is evaluated, which takes O(1) time.
On 1/i of these iterations, loop C iterates j times, doing O(1) work per iteration. Since j is O(i2) on average, and this is only done for 1/i iterations of loop B, the average cost is O(i2 / i) = O(i).
Multiplying all of this together, we get O(n × i2 × (1 + i)) = O(n × i3). Since i is on average O(n), this is O(n4).
The tricky part of this is saying that the if condition is only true 1/i of the time:
Basically, why can't j % i go up to i * i rather than i?
In fact, j does go up to j < i * i, not just up to j < i. But the condition j % i == 0 is true if and only if j is a multiple of i.
The multiples of i within the range are i, 2*i, 3*i, ..., (i-1) * i. There are i - 1 of these, so loop C is reached i - 1 times despite loop B iterating i * i - 1 times.
The first loop consumes n iterations.
The second loop consumes n*n iterations. Imagine the case when i=n, then j=n*n.
The third loop consumes n iterations because it's executed only i times, where i is bounded to n in the worst case.
Thus, the code complexity is O(n×n×n×n).
I hope this helps you understand.
All the other answers are correct, I just want to amend the following.
I wanted to see, if the reduction of executions of the inner k-loop was sufficient to reduce the actual complexity below O(n⁴). So I wrote the following:
for (int n = 1; n < 363; ++n) {
int sum = 0;
for(int i = 1; i < n; ++i) {
for(int j = 1; j < i * i; ++j) {
if(j % i == 0) {
for(int k = 0; k < j; ++k) {
sum++;
}
}
}
}
long cubic = (long) Math.pow(n, 3);
long hypCubic = (long) Math.pow(n, 4);
double relative = (double) (sum / (double) hypCubic);
System.out.println("n = " + n + ": iterations = " + sum +
", n³ = " + cubic + ", n⁴ = " + hypCubic + ", rel = " + relative);
}
After executing this, it becomes obvious, that the complexity is in fact n⁴. The last lines of output look like this:
n = 356: iterations = 1989000035, n³ = 45118016, n⁴ = 16062013696, rel = 0.12383254507467704
n = 357: iterations = 2011495675, n³ = 45499293, n⁴ = 16243247601, rel = 0.12383580700180696
n = 358: iterations = 2034181597, n³ = 45882712, n⁴ = 16426010896, rel = 0.12383905075183874
n = 359: iterations = 2057058871, n³ = 46268279, n⁴ = 16610312161, rel = 0.12384227647628734
n = 360: iterations = 2080128570, n³ = 46656000, n⁴ = 16796160000, rel = 0.12384548432498857
n = 361: iterations = 2103391770, n³ = 47045881, n⁴ = 16983563041, rel = 0.12384867444612208
n = 362: iterations = 2126849550, n³ = 47437928, n⁴ = 17172529936, rel = 0.1238518469862343
What this shows is, that the actual relative difference between actual n⁴ and the complexity of this code segment is a factor asymptotic towards a value around 0.124... (actually 0.125). While it does not give us the exact value, we can deduce, the following:
Time complexity is n⁴/8 ~ f(n) where f is your function/method.
The wikipedia-page on Big O notation states in the tables of 'Family of Bachmann–Landau notations' that the ~ defines the limit of the two operand sides is equal. Or:
f is equal to g asymptotically
(I chose 363 as excluded upper bound, because n = 362 is the last value for which we get a sensible result. After that, we exceed the long-space and the relative value becomes negative.)
User kaya3 figured out the following:
The asymptotic constant is exactly 1/8 = 0.125, by the way; here's the exact formula via Wolfram Alpha.
Remove if and modulo without changing the complexity
Here's the original method:
public static long f(int n) {
int sum = 0;
for (int i = 1; i < n; i++) {
for (int j = 1; j < i * i; j++) {
if (j % i == 0) {
for (int k = 0; k < j; k++) {
sum++;
}
}
}
}
return sum;
}
If you're confused by the if and modulo, you can just refactor them away, with j jumping directly from i to 2*i to 3*i ... :
public static long f2(int n) {
int sum = 0;
for (int i = 1; i < n; i++) {
for (int j = i; j < i * i; j = j + i) {
for (int k = 0; k < j; k++) {
sum++;
}
}
}
return sum;
}
To make it even easier to calculate the complexity, you can introduce an intermediary j2 variable, so that every loop variable is incremented by 1 at each iteration:
public static long f3(int n) {
int sum = 0;
for (int i = 1; i < n; i++) {
for (int j2 = 1; j2 < i; j2++) {
int j = j2 * i;
for (int k = 0; k < j; k++) {
sum++;
}
}
}
return sum;
}
You can use debugging or old-school System.out.println in order to check that i, j, k triplet is always the same in each method.
Closed form expression
As mentioned by others, you can use the fact that the sum of the first n integers is equal to n * (n+1) / 2 (see triangular numbers). If you use this simplification for every loop, you get :
public static long f4(int n) {
return (n - 1) * n * (n - 2) * (3 * n - 1) / 24;
}
It is obviously not the same complexity as the original code but it does return the same values.
If you google the first terms, you can notice that 0 0 0 2 11 35 85 175 322 546 870 1320 1925 2717 3731 appear in "Stirling numbers of the first kind: s(n+2, n).", with two 0s added at the beginning. It means that sum is the Stirling number of the first kind s(n, n-2).
Let's have a look at the first two loops.
The first one is simple, it's looping from 1 to n. The second one is more interesting. It goes from 1 to i squared. Let's see some examples:
e.g. n = 4
i = 1
j loops from 1 to 1^2
i = 2
j loops from 1 to 2^2
i = 3
j loops from 1 to 3^2
In total, the i and j loops combined have 1^2 + 2^2 + 3^2.
There is a formula for the sum of first n squares, n * (n+1) * (2n + 1) / 6, which is roughly O(n^3).
You have one last k loop which loops from 0 to j if and only if j % i == 0. Since j goes from 1 to i^2, j % i == 0 is true for i times. Since the i loop iterates over n, you have one extra O(n).
So you have O(n^3) from i and j loops and another O(n) from k loop for a grand total of O(n^4)

Assuming that n is a positive even integer, what will be the value of x right after this segment of code is run?

x = 0;
for (i = 1; i <= n/2; i++) {
for (j = 1; j <=n; j++) {
if (j > i)
x++;
}
}
I'm trying to predict the value of x by capturing a summation but I'm kind of stuck because I know that for each iteration of the first for loop, the summation changes for the inner loop. For example if we assume x is 10, after the first completion of the inner loop, x would have 9, then after the 2nd completion, we add 8 to x, then 7, 6, etc. The final value of x would be 35. How would I represent this in a cohesive equation for any positive even integer n?
Skip to the end for a simple equation; here I show the steps you might take.
First, here's the original code:
x = 0;
for (i = 1; i <= n/2; i++) {
for (j = 1; j <=n; j++) {
if (j > i)
x++;
}
}
We can start j at i+1 to skip a lot of pointless loops
x = 0;
for (i = 1; i <= n/2; i++) {
for (j = i+1; j <=n; j++) {
if (j > i)
x++;
}
}
Then instead of adding 1 on each of n-i loops, we can just add n-i.
x = 0;
for (i = 1; i <= n/2; i++) {
x += (n-i)
}
That's the same as this (just writing out what we're adding in the loops):
x = (n-1) + (n-2) + ... + (n - n/2)
We can pull out the n's.
x = n * (n/2) - 1 - 2 - 3 - ... - n/2
The final simplification is for the summation of 1 through n/2.
x = n * (n/2) - ((n/2) * (n/2 + 1))/2

Difference between the implementation?

So recently I stumbled across a query i.e.
code 1:
for(long i = 1; i <= m; i++) {
long j = (fullsum - 2*(sum -i))/2;
if(j >= m+1 && j <=n) {
swaps++;
}
}
code 2:
for(long i = 1; i <= m; i++) {
for(long j = m+1; j <=n ; j++) {
if(sum - i + j == fullsum - sum -j + i) {
swaps++;
break;
}
}
}
Where fullsum = n*(n+1)/2, sum = m*(m+1)/2
1 <= N <= 10^9
1 <= M < N
Now my question here is that both the codes seem identical to me(logic wise) but Code 2 is giving correct output while code 1 is not.
Can anyone please tell me the difference between the codes, further why code2 is giving the correct output while code1 is not and what is the correct way of implementing code 1?
Rewriting if(sum - i + j == fullsum - sum -j + i), we get
if(2*j == fullsum - 2*(sum-i))
In the first code, the value you are assigning to j is
long j = (fullsum - 2*(sum -i))/2;
The issue is pretty clear: division truncation is causing the incorrect results. Let's say that fullsum - 2*(sum-i) = 45 for some case, and j=22. Now, the second condition will be false, since 2*j != fullsum - 2*(sum-i).
However, for the first condition, (fullsum - 2*(sum -i))/2 has a value of 45/2 = 22 (floor division), so the condition j = (fullsum - 2*(sum -i))/2 will be counted as a valid result when it shouldn't have been.

Running Time of Nested Loops

I am sure the running time of this nested loop is O(N*log(N)). The running time of the inner loop is log(N) and the outher loop is N.
for (int i = 0; i < N; ++i) {
for (int j = 1; j <= i; j *= 2) {
}
}
In the inner Loop what if I change j *= 2 to j *= 3. How is the result going to change in this case?
#Kevin is completely right, but I thought I would show some experimental results. You can easily test this out by creating a counter that gets incremented inside each inner loop iteration and running for different values of N. Then a fit can be made of the form time = a * N * log(N). For the case j *= 2, we get a coefficient a = 1.28. For j *= 3, we get a = 0.839.
I generated this figure using the MATLAB script below:
clear
clc
close all
nmin = 10;
nmax = 1000;
count1 = zeros(nmax - nmin + 1, 1);
for n = nmin: nmax
k = 0;
for i = 0: n - 1
j = 1;
while (j <= i)
j = j * 2;
k = k + 1;
end
end
count1(n - nmin + 1) = k;
end
ns = (nmin: nmax)';
figure
hold on
plot(ns, count1, '--')
a1 = mean(count1 ./ (ns .* log(ns)))
fit1 = a1 * ns .* log(ns);
plot(ns, fit1)
%%
count2 = zeros(nmax - nmin + 1, 1);
for n = nmin: nmax
k = 0;
for i = 0: n - 1
j = 1;
while (j <= i)
j = j * 3;
k = k + 1;
end
end
count2(n - nmin + 1) = k;
end
ns = (nmin: nmax)';
plot(ns, count2, '-.')
a2 = mean(count2 ./ (ns .* log(ns)))
fit2 = a2 * ns .* log(ns);
plot(ns, fit2)
xlabel('N')
ylabel('Time complexity')
legend('j *= 2', 'j *= 2 fit', 'j *= 3', 'j *= 3 fit', 'Location', 'NorthWest')
It will still be logarithmic. However, it will be scaled by a constant factor (which is irrelevant in Big O analysis).
The effect is that the base of the logarithm changes (see https://en.wikipedia.org/wiki/Logarithm#Change_of_base).
----------[ j = 2 * j ] for j < i:-------------
j = 2*1 = 2 =>2^1
2*2 = 4 =>2^2
2*4 = 8 =>2^3
............. 2^k = n say n==i
if log applied on both side 2^k = n
log(2^k) = logn
k = log_2(N) where 2 is the base
----------[ j = 3 * j ] for j < i:-------------
j = 3*1 = 3 =>3^1
3*3 = 9 =>3^2
3*9 = 27 =>2^3
.............loop stop when 3^k = n say n==i
if log applied on both side 3^k = n
log(3^k) = logn
k = log_3(N) where 3 is the base

What will be the Worst case Time complexity for this?

for(int i = 1 ; i < n ; i* = 2)
for(int j = 1 ; j < i ; j* = 2)
Can anyone explain me this?
I think it is log(n)*log(i) .Is that correct?
Assuming
for (i = 1; i < n; i *= 2)
for (j = 1; j < i; j *= 2)
...stuff...
"stuff" will be run 1 + 2 + 3 + ... + log(n)-1 times. Since the sum of integers 1 to N is N * (N + 1) / 2, worse case run time is O(log(n) ^ 2).

Resources