What is the time complexity of this for loop (be related to `n`)?

Viewed 202

What is the time complexity of this for loop (be related to n)?

for(int i = 1, j; i <= n; i = j + 1)
{
        j = n / (n / i);
}

Please note that i, j and n are integer variables and they follow integer arithmetic. In particular, the expression n/(n/i) inside the loop should be interpreted as below:

enter image description here

4 Answers

If we use j = i; instead of j = n / (n / i);, the time complexity is O(n). Now it's j = n / (n / i);, suppose that n = i*k+r, where k and r are all integers and r = n%i. Thus j = (i*k+r)/((i*k+r)/i) = (i*k+r)/k = i+r/k >= i, which means i will increment faster than the case where you use j = i;. So at least the time complexity is less than O(n), which I suppose gives you another O(n).

And besides the big O notation, there are another two notations(Θ and Ω) which means the lower and upper bound of O(n). You can get time complexity by finding these two bounds. And there's another rule if I remember correctly, O(k*n) = O(n), the coefficient k doesn't matter no matter how big it is.

As elaborated by taotsi, the increment for i in each iteration is

inc = 1 + r/k

where r=n%i and k=n/i. Since r<i, the increment is 1 as long as i<sqrt(n) (because then i*i/n<1 become 0 in integer division). Thereafter, the increment is (typically) 2 as long as i<2*sqrt(n). This continues similar to the geometric series, giving a factor 2 over sqrt(n), i.e. 2 sqrt(n) iterations.

If we write n = a*a+b with integers 0 <= b <= 2*a (i.e. a=int(sqrt(n)) and b=n-a*a), then the total number of iterations in simple experiments is always

b < a?  2*a-1 : 2*a

Thus, the complexity is O(√n) (provided some useful work is done inside the loop, for example counting the number of total iterations, such that the compiler is not allowed to elide the whole loop).

As @Walter has already offered a proof, I am too late for that part, but here is a Python3 version of your code and a plot of the number of iterations as a function of n vs the 2*sqrt(n) function. They look approximately the same (up to n = 1e9).

import matplotlib.pyplot as plt
from numba import jit
import math

@jit
def weird_increment_loop(n):
    i = 1
    j = 0
    iterations = 0
    while i <= n:
        j = n // (n // i)
        i = j + 1
        iterations = iterations + 1

    return iterations

iterations = []
func_2sqrt = []
domain = range(0,1000000001,1000000)
for n in domain:
    iterations.append(weird_increment_loop(n))
    func_2sqrt.append(math.sqrt(n)*2)

plt.plot(domain,iterations)
plt.plot(domain,func_2sqrt)
plt.xlabel("n")
plt.ylabel("iterations(n) and 2*sqrt(n)")
plt.show()

Here is the plot: enter image description here

If you see no difference, it is because there is close to none :D Of course, one should always trust Mathematics ;)

Strictly by the rules of C++, it's O(1). Either the loop terminates after some finite amount of doing no observable work, or it loops forever (which is undefined behaviour). A conforming implementation may assume that undefined behaviour is not encountered, so we may assume it terminates.

Because the observable effects of the program does not depend on what happens inside the loop, an implementation is allowed to "As-if" it into nothingness.

Related