为什么我在这两个不同的嵌套for循环中得到不同的时间复杂度?

时间:2017-03-26 13:51:55

标签: java performance time-complexity big-o nested-loops

好的,所以我知道两个嵌套的for循环,每个递增1给出了二次时间复杂度。然后我很好奇,看看我是否更改了其中一个循环的更新,增加了2乘以我得到O(n log n)而不是O(n ^ 2),反之亦然到另一个循环。

在每个内部循环中,我有一个变量来计算循环执行的次数。该数组的大小为2 ^ 20,因此为1,048,576。我认为这两种方法应该具有相同的n log n复杂度(20 * 1,048,576)。但只有算法2接近这种复杂性,算法1的复杂度为n * 2.

据我所知,一个循环是O(n),另一个循环是O(log n)所以它应该是O(n log n),然后如果我切换它们我应该得到O(log nn)的复杂性这将是一回事。

 int[] arr = new int[1048576];

    // Algorithm 1
    int counter1 = 0;
    for (int i = 1; i < arr.length; i++) {
        for (int j = i; j < arr.length; j *= 2) {
            counter1++;
        }
    }
    System.out.println(counter1);

    // Algorithm 2
    int counter2 = 0;
    for (int i = 1; i < arr.length; i *= 2) {
        for (int j = i; j < arr.length; j++) {
            counter2++;
        }
    }
    System.out.println(counter2);

    // counter1: 2097130 (n * 2)
    // counter2: 19922945 (n log n)

3 个答案:

答案 0 :(得分:0)

简单的数学。现在假设,w *= 2基本上需要20步才能达到大约100万。

Algo 1将运行大约100万个j循环,但这些循环每个只需要大约20个j步完成。你在算法2上运行内循环(特别是第一次),数百万,而另一个运行&lt; = 20次,100万次。 但是,您需要考虑衰减,尤其是在开始时。当你点击i=2时,你的算法已经下降到19个j步。到4岁时,你会降到18岁,依此类推。这种早期的衰变将基本上“扼杀”步数的势头。最后~500,000“i-steps”只会增加一次计数器。

Algo 2在第一次运行时运行100万个j步,然后是另一个(i-step-1)j步(1,然后是2,接着是4,等等)。你每次都大致跑了一百万步。

答案 1 :(得分:0)

让我们计算第二个算法的每个循环中的遍数。我们来看N=arr.length

首先是最外面的循环。 i的范围从1到N,每次乘以2,即log(N)次迭代。

然后,在最内层循环中,j的范围从i到N并且每次递增1,这使得(N-i)次迭代。

现在让我们k=log(i)。因此,counter2递增的总次数为sum(N-2^k) for k=0 to log(N)

k = 0到log(N)的2 ^ k的和是几何和,其加起来为2 ^(log(N)+1)-1,因此2N-1。

因此,第二个循环的总复杂度是Nlog(N)-2N + 1,就像你的第一个循环一样是O(Nlog(N))。

差异是二阶项2N。如果我们推动我们的开发,我们对第一个循环的复杂性是:

Nlog(N) + O(1)

和第二个:

Nlog(N) - 2N + O(1)

答案 2 :(得分:-1)

你发布的两个解决方案都具有完全相同的复杂性,我假设你忘了交换内部的for循环开始子句变量。

O(n ^ 2)具有常数因子。 Big-O符号的工作原理如下:

n / 2 * n是你的循环所需的时间(你发布的两个中的任何一个)

- &GT; 1/2 * n * n = 1/2 * n ^ 2

1/2是因素。 复杂性是多项式,n ^ 2