以下是我提出的算法的一部分:
for (int i = 0; i < n - 1; i++)
for (int j = i; j < n; j++)
(...)
我正在使用这个“双循环”来测试大小为n的数组中所有可能的2元素和。
显然(我必须同意),这个“双循环”是O(n²)
:
n + (n-1) + (n-2) + ... + 1 = sum from 1 to n = (n (n - 1))/2
这是我感到困惑的地方:
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
(...)
第二个“双循环”也具有O(n²)
的复杂度,当它明显(最差)比第一个更好(?)时。
我错过了什么?信息准确吗?有人可以解释这个“现象”吗?
答案 0 :(得分:4)
(n (n - 1))/2
简化为n²/2 - n/2
。如果对n
使用非常大的数字,n/2
的增长率与n²
相比会相形见绌,因此为了计算Big-O复杂度,您实际上会忽略它。同样,&#34;常数&#34;当n
增加时,1/2的值不会增长,所以你也忽略它。这只会让你n²
。
请记住,复杂度计算与&#34;速度&#34;不同。一种算法可能比另一种算法慢五千倍,并且仍具有较小的Big-O复杂度。但是当你将n
增加到非常大的数字时,会出现一般模式,通常可以使用简单的公式进行分类:1
,log n
,n
,n log n
, n²
等等。
创建图表并查看显示的行类型有时会有所帮助:
即使这两个图的缩放系数非常不同,您也可以看到它产生的曲线类型几乎完全相同。
答案 1 :(得分:2)
常数因素。
Big-O表示法忽略常数因子,因此即使第二个循环因常数因子较慢,它们也会以相同的时间复杂度结束。
在the definition中,它告诉您可以选择任何旧的常数因子:
...当且仅当存在正常数M ...
这是因为我们想要分析算法的增长率 - 常数因素只会使事情复杂化并且通常依赖于系统(操作在不同机器上的持续时间可能不同)。
你可以只计算某些类型的操作,但问题就变成了选择哪个操作,以及如果该操作在某些算法中不占主导地位。然后你需要将操作相互关联(以独立于系统的方式,这可能是不可能的),或者你可以为每个操作分配相同的权重,但是这将是相当不准确的,因为一些操作会显着比其他人更长。
说O(15n² + 568n + 8 log n + 23 sqrt(n) + 17)
(例如)真的有用吗?而不仅仅是O(n²)
。
(出于以下目的,假设为n >= 2
)
请注意,我们实际上这里的渐近变小(即,我们接近无穷大)这些术语,但我们总是可以将其简化为常数因素。 (It's n(n+1)/2
, not n(n-1)/2
)
n(n+1)/2 = n²/2 + n/2
and
n²/2 <= n²/2 + n/2 <= n²
鉴于我们刚刚显示n(n+1)/2
位于C.n²
和D.n²
之间,对于两个常数C
和D
,我们&#39 ; ve也刚刚表明它是O(n²)
。
注意 - big-O表示法实际上严格来说是一个上限(所以我们只关心它比一个函数小,而不是两个之间),但它通常用来表示{{1} (big-Theta),它关心两个边界。
答案 2 :(得分:0)
在典型用法中,不使用O表示法的正式定义 直;相反,函数f的O表示法是由 遵循简化规则:
如果f(x)是几个项的总和,那么 保留增长率最大的一个,其他所有省略
Big-O仅用于给出渐近行为 - 一个比另一个更快进入它 - 它们都是O(N ^ 2)
答案 3 :(得分:0)
你也可以说第一个循环是O(n(n-1)/ 2)。 big-O的奇特数学定义类似于:
如果存在常数c,n,则函数“f”是函数“g”的大-O,使得f(x)< c * g(x)表示某些c和所有x> ñ。这是一种奇特的方式,说g在某个点上面是一个上限,并且有一些常量应用。然后,O(n(n-1)/ 2)= O((n ^ 2-n)/ 2)是O(n ^ 2)的大O,这对于快速分析来说更加简洁。
答案 4 :(得分:0)
AFAIK,您的第二个代码段
for(int i = 0; i < n; i++) <-- this loop goes for n times
for(int j = 0; j < n; j++) <-- loop also goes for n times
(...)
基本上,它的时间复杂度O(n*n) = O(n^2)
。
根据BIG-O理论,常数因子被忽略,只考虑更高阶。这就是说,如果复杂性为O(n^2+k)
,那么实际复杂性将O(n^2)
常量k
将被忽略。
(OR)如果复杂度为O(n^2+n)
,则实际复杂度为O(n^2)
,将忽略低阶n
。
因此,在第一种情况下,复杂度为O(n(n - 1)/2)
将/可简化为
O(n^2/2 - n/2) = O(n^2/2) (Ignoring the lower order n/2)
= O(1/2 * n^2)
= O(n^2) (Ignoring the constant factor 1/2)