O(N log N)复杂度-与线性相似吗?


78

所以我想问这个琐碎的问题会被埋葬,但是我对某些事情有些困惑。

我已经用Java和C实现了quicksort,并且正在做一些基本的比较。该图显示为两条直线,C超过Java对应的100,000个随机整数快4毫秒。

结果

我的测试代码可以在这里找到;

android基准测试

我不确定(n log n)行是什么样子,但我不认为这是直线。我只是想检查这是否是预期的结果,并且我不应该尝试在我的代码中发现错误。

我将公式粘贴到excel中,对于以10为底的基数,它似乎是一条直线,一开始就有些扭结。这是因为log(n)和log(n + 1)之差线性增加吗?

谢谢,

加夫


1
Google图片搜索在诸如“ n log n”之类的搜索中似乎出奇地好。
Tom Hawtin-定位线

1
最上面的Java语言对我来说并不直接。
小狗

确实确实如此,这就是为什么将其称为“线性时间”的原因
msanford

Answers:


81

放大图表,您会发现O(n logn)并不是一条直线。但是,是的,它几乎接近线性行为。要知道为什么,只需取几个非常大的对数即可。

例如(以10为底):

log(1000000) = 6
log(1000000000) = 9
…

因此,要对1,000,000个数字进行排序,O(n logn)排序会添加一个小数6(因为大多数排序算法将依赖于以2为底的对数,所以位数增加了6)。不是很多。

事实上,该日志的因素是如此非常小,对于大小的大部分订单,确立为O(n LOGN)算法优于线性时间的算法。一个突出的例子是创建后缀数组数据结构。

最近,当我尝试通过使用基数排序改进短字符串的快速排序排序时,一个简单的案例使我很受困扰。事实证明,对于短字符串,这种(线性时间)基数排序要比快速排序快,但是对于相对较短的字符串而言,还是有一个转折点,因为基数排序主要取决于您排序的字符串的长度。


1
好的分类一旦被划分并征服成足够小的块,就倾向于采用线性算法。基准测试(实际数据)到底有多小。
汤姆·哈特芬

2
汤姆:我不确定线性是什么意思。通常,排序算法会使用O(n ^ 2)排序(例如对小部分进行插入排序)进行相反的操作,因为它们的常数因子非常小,以至于二次运行时也胜于nlogn排序。另一方面,introsort使用一种策略来突破太深的递归-但是,这又不是线性的,它只是将四进制最坏情况交换为O(n logn)行为。
康拉德·鲁道夫2009年

11

仅供参考,快速排序实际上为O(n ^ 2),但平均情况为O(nlogn)

仅供参考,O(n)和O(nlogn)之间有很大的不同。这就是为什么它不受任何常量O(n)约束的原因。

有关图形演示,请参见:

O(n)vs O(nlogn)


2
a)未指定时,通常使用O()表示预期的(平均)复杂度。b)O()表示法不包含常数因子,因此O(n)和O(2n)相同。由于log(n)几乎是常数(与n相比,为大数),因此可以说O(n)和O(n log(n))几乎相同。您应该已经绘图:wolframalpha.com/input/?i=plot+
+x%2C+x+log+x+from

13
这通常是不正确的。Big O表示法通常表示最坏情况下的渐近复杂度,并且它表示的函数超出算法的复杂度。O(n)并不近似于O(nlogn),尽管出于实用目的,O(nlogn)相对较好,但也不差。快速分类的最坏情况当然不是一件容易的事。如果您不相信我,请尝试对字典中的条目进行快速排序。
土拨鼠2010年

我没事有很大的不同。特别是当您将其与下一个订单$ O(n ^ 2)$进行比较时。i.sli.mg/9zXUQR.png
等腰波振荡

5

为了以类似的方式获得更多乐趣,请尝试在标准不相交集数据结构上绘制n次操作所花费的时间。它已被证明是渐近ñ  α(ñ)其中α(ñ)是的逆阿克曼功能(虽然你平时的算法教科书可能只显示一个必然的ñ log日志ñ或可能ñ日志* ñ)。对于任何种类的数量,你将有可能遭遇的输入大小,α(ñ)≤5(事实上日志*  ñ  ≤5),尽管它的确趋近于无穷渐近。  

我想您可以从中学到的是,虽然渐进复杂性是思考算法的非常有用的工具,但它与实际效率并不完全相同。


3
  1. 通常,O(n * log(n))算法具有2基对数实现。
  2. 对于n = 1024,log(1024)= 10,因此n * log(n)= 1024 * 10 = 10240计算,增加了一个数量级。

因此,O(n * log(n))仅对于少量数据类似于线性。

提示:请不要忘记,快速排序在随机数据上的表现非常好,并且它不是O(n * log(n))算法。


3
所有对数都是相同的,只是比例不同。因此,我看不出您的第一句话的重要性。另外,我也不同意您的陈述,即O(n log n)仅类似于线性的少量数据。再一次,这是一个扩展的事情。作为反例,只需查看原始问题中的图形即可。
蜡翼

我的意思不是图形上相似(与直线相似),而是时间复杂度相似。O(n logn)时间很容易比O(n)大一个数量级。如果图形比较O(n logn)和O(n)算法,您会明白我的意思。:)随着N越来越大,O(n logn)*移至下一个对数刻度。
Nick Dandoulakis 09年

1
平均而言,Quicksort是一种O(n log n)算法。
Manu

如@waxwing所述,第一点是错误的。通过划分不同基准的限制,您可以证明更改对数基准仅会以恒定因子影响复杂度-从复杂度角度来看,对数基准无关紧要。至于结论,随着元素数量的增加,线性和对数线性线的形状将变得越来越相似,而不是更少。
主教

2

如果正确选择了轴,任何数据都可以绘制在一条线上:-)

维基百科说Big-O是最坏的情况(即f(x)为O(N)意味着f(x)被N限制在上方)https://en.wikipedia.org/wiki/Big_O_notation

这是一组不错的图表,描述了各种常用功能之间的差异:http : //science.slc.edu/~jmarshall/courses/2002/spring/cs50/BigO/

log(x)的导数为1 / x。这就是log(x)随着x的增加而增加的速度。它不是线性的,尽管它看起来像一条直线,因为它弯曲得太慢了。考虑O(log(n))时,我将其视为O(N ^ 0 +),即N的最小乘方不是常数,因为N的任何正恒定乘方最终都会超过它。这不是100%准确的,所以如果您这样解释,教授会生气的。

两个不同基数的对数之间的差异是一个常数乘数。查找用于在两个基数之间转换日志的公式:(在此处的“基数更改”下:https : //en.wikipedia.org/wiki/Logarithm)技巧是将k和b视为常量。

实际上,在您绘制的任何数据中通常都会出现一些问题。程序外的东西会有所不同(某些东西会在程序之前交换到cpu中,缓存未命中等)。要花费大量时间才能获得可靠的数据。常量是试图将Big O表示法应用于实际运行时的最大敌人。对于足够小的N,具有高常数的O(N)算法可能比O(N ^ 2)算法慢。


(我假设您的意思是一条直线,而不是“直线”作为曲线的总称。)我要买的是,如果轴是实轴,则实变量的任何连续可微分,实值函数都可以绘制在一条直线上正确地选择了只有中等的恶作剧,例如重复的轴值(除非是一对一的函数,否则是必需的),但是“任何数据”?我认为这很麻烦。对于所有有理数为零而对于所有无理数为零的函数呢?(这就是Dirichlet函数,它是一个真正的数学函数。)
Sarah G

1

log(N)大约是(非常)N中的位数。因此,在大多数情况下,log(n)和log(n + 1)之间几乎没有区别


3
log-base- 10大约是N的位数(假设您使用的是十进制表示形式)。大多数排序/搜索算法将使用log-base-2,尽管它与log-base-10成比例(因此big-O仍然适用),却与您所描述的
不符

说它的另一种方式是,对数底数为2的大致的中N个数位的数目写在二进制的情况下,也称为所需的比特数来表示N.
泰勒McHenry的

0

尝试在其上面绘制一条实际的线性线,您会看到很小的增加。注意,在50,0000处的Y值小于在100,000处的1/2 Y值。

它在那里,但是很小。这就是为什么O(nlog(n))这么好!


仍然比O(n ^ 2)更好。
paxdiablo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.