好的,所以我知道两个嵌套的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)
答案 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