Java 为什么计算复杂度是O(n^4)? int和=0; 对于(int i=1;i
我不明白当j=I,2i,3i。。。的最后一个Java 为什么计算复杂度是O(n^4)? int和=0; 对于(int i=1;i,java,big-o,Java,Big O,我不明白当j=I,2i,3i。。。的最后一个循环运行n次。我想我只是不明白我们是如何根据if语句得出这个结论的 编辑:我知道如何计算所有循环的复杂度,除了为什么最后一个循环基于mod运算符执行I次。。。我只是不明白这是怎么回事。基本上,为什么j%i不能上升到i*i而不是i? 第一个循环消耗n迭代 第二个循环消耗n*n迭代。想象一下当i=n,然后j=n*n时的情况 第三个循环消耗n迭代,因为它只执行i次,其中i在最坏的情况下被限制为n 因此,代码复杂度为O(n×n×n×n) 我希望这有助于您理解
循环运行n次。我想我只是不明白我们是如何根据if
语句得出这个结论的
编辑:我知道如何计算所有循环的复杂度,除了为什么最后一个循环基于mod运算符执行I次。。。我只是不明白这是怎么回事。基本上,为什么j%i不能上升到i*i而不是i?
- 第一个循环消耗
n
迭代
- 第二个循环消耗
n*n
迭代。想象一下当i=n
,然后j=n*n
时的情况
- 第三个循环消耗
n
迭代,因为它只执行i
次,其中i
在最坏的情况下被限制为n
因此,代码复杂度为O(n×n×n×n)
我希望这有助于您理解。让我们看看前两个循环
第一个很简单,从1循环到n。第二个更有趣。从1到i的平方。让我们看一些例子:
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
总之,i和j循环
组合起来有1^2+2^2+3^2
有一个计算前n个平方和的公式,n*(n+1)*(2n+1)/6
,大致是O(n^3)
最后有一个k循环
,它从0循环到j
当且仅当j%i==0
。由于j
从1变为i^2
,j%i==0
对于i
次为真。由于i循环
在n
上迭代,因此您有一个额外的O(n)
因此,您有来自i和j循环的O(n^3)
,还有来自k循环的O(n)
,总共O(n^4)
让我们标记循环a、B和C:
int和=0;
//环路A
对于(int i=1;i
- 循环A迭代O(n)次
- 循环B在A的每次迭代中迭代O(i2)次。对于每个迭代:
- 计算
j%i==0
,需要O(1)个时间
- 在这些迭代的1/i上,循环C迭代j次,每次迭代做O(1)个工作。因为j的平均值是O(i2),并且这只在循环B的1/i迭代中进行,所以平均成本是O(i2/i)=O(i)
将所有这些相加,我们得到O(n×i2×(1+i))=O(n×i3)。因为i的平均值是O(n),所以这是O(n4)
这其中的棘手部分是说,if
条件只有1/i的时间是真的:
基本上,为什么j%i不能上升到i*i而不是i
事实上,j
确实上升到j
,而不仅仅是上升到j
。但是当且仅当j
是i
的倍数时,条件j%i==0
为真
范围内的i
倍数为i
,2*i
,3*i
,…,(i-1)*i
。其中有i-1
,因此循环C达到i-1
次,尽管循环B迭代i*i-1
次。所有其他答案都是正确的,我只想修正以下内容。
我想看看,减少内部k循环的执行是否足以降低O(n)以下的实际复杂性⁴).代码>所以我写了以下内容:
for(int n=1;n<363;++n){
整数和=0;
对于(int i=1;i
执行此操作后,很明显,复杂性实际上是n⁴代码>。输出的最后几行如下所示:
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
这表明,实际n之间的实际相对差异⁴
这个代码段的复杂度是一个因子,它的值大约是0.124…
(实际上是0.125)。虽然它没有给出准确的值,但我们可以推断如下:
时间复杂度为n⁴/8~f(n)
其中f
是您的函数/方法
- 关于大O表示法的维基百科页面在“Bachmann–Landau表示法家族”的表格中指出,
~
定义了两个操作数边的限制相等。或者:
f渐近地等于g
(我选择363作为排除上限,因为n=362
是我们得到合理结果的最后一个值。之后,我们超出了长空间,相对值变为负值。)
用户kaya3得出以下结论:
顺便说一下,渐近常数正好是1/8=0.125
删除if
和模,而不改变复杂性
以下是原始方法:
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;
}
为了更容易地计算复杂性,您可以引入一个中间变量j2
,以便每个循环变量在每次迭代时递增1:
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;
}
它显然与原始代码的复杂性不同,但它返回的值是相同的
如果你在谷歌上搜索第一个术语,你会发现0211
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;
}
public static long f4(int n) {
return (n - 1) * n * (n - 2) * (3 * n - 1) / 24;
}