[英]Why is the performance of these two functions so drastically different despite the same Big-O?
我正在学习 Big-O 表示法,而不是 class,只是通过自己阅读来学习。 我正在阅读 Pythonds 并进行了练习,您的任务是编写“非最佳” Python function 以找到列表中的最小数字。 function 应该将每个数字与列表中的每个其他数字进行比较:O(n**2)。
这是我想出的:
def myFindmin(alist):
return[x for x in alist if all(x<=y for y in alist)][0]
这就是我正在阅读的书给出的内容:
def findmin(alist):
overallmin=alist[0]
for i in alist:
issmallest=True
for j in alist:
if i>j:
issmallest=False
if issmallest:
overallmin=i
return overallmin
显然,书本版本削减了更多变量等,但根据我所学到的,这两个函数的 Big-O 表示法应该是 O(n**2),不是吗? 他们都将每个数字与其他数字进行比较,这使得 n**2 成为 function 的主要部分,是吗?
但是,当我将 myFindmin() function 与本书的 findmin() function 与最佳 min() function 进行比较时,我得到了三个非常不同的结果:
if __name__=='__main__':
for l in range(1000,10001,1000):
thelist=[randrange(100000)for x in range(l)]
print('size: %d'%l)
for f in[findmin,myFindmin,min]:
start=time()
r=f(thelist)
end=time()
print(' function: %s \ttime: %f'%(f.__name__,end-start))
他们甚至没有接近:
...
size: 8000
function: findmin time: 1.427166
function: myFindmin time: 0.004312
function: min time: 0.000138
size: 9000
function: findmin time: 1.830869
function: myFindmin time: 0.005525
function: min time: 0.000133
size: 10000
function: findmin time: 2.280625
function: myFindmin time: 0.004846
function: min time: 0.000145
如您所见,myFindmin() 不如最优线性 O(n) min() function 快,但仍比 O(n**2) findmin() function 快得多。 我认为 myFindmin 应该是 O(n**2) 但它似乎既不是 O(n) 也不是 O(n**2) 那么这里到底发生了什么? myFindmin 的 Big-O 是什么?
更新
如果我在 all 语句的嵌套循环中添加括号:
def myFindmin(alist):
return[x for x in alist if all([x<=y for y in alist])][0]
这实际上使 myFindmin 始终比 findmin 慢:
size: 8000
function: findmin time: 1.512061
function: myFindmin time: 1.846030
function: min time: 0.000093
size: 9000
function: findmin time: 1.925281
function: myFindmin time: 2.327998
function: min time: 0.000104
size: 10000
function: findmin time: 2.390210
function: myFindmin time: 2.922537
function: min time: 0.000118
所以这里发生的是,在原始的 myFindmin 中,整个列表不是通过列表推导生成的,它实际上是由 all() 本身通过生成器表达式生成的,该生成器表达式也在执行惰性求值,这意味着它一旦停止求值并生成找到一个假值。
如果我添加括号,那么会发生什么,列表会通过不执行惰性评估的列表理解生成和评估。 每次在传递给 all() 进行惰性重新评估之前,都会生成整个列表。
由于原始 myFindmin() 的 Big-O 为 O(nlogn),新的 myFindmin()(带括号)将具有 O(n^2+nlogn) 的 Big-O,这反映在结果时间中。 有关为什么原始 myFindmin() 为 O(nlogn) 的解释,请参阅我已标记为最佳答案的 Amit 的答案。
谢谢大家!
您的代码实际上是O(nlogn)
(平均情况)而不是O(n^2)
。
看看all(x<=y for y in alist)
,回想一下,要产生False
,一个元素大于x
就足够了,不需要 go 通过alist
的所有值。
假设您的列表是随机(且均匀地)打乱的,让我们检查需要遍历多少元素:
x is the highest element: traverse n elements
x is the 2nd highest element: traverse n/2 elements
x is the 3rd highest element: traverse n/3 elements
....
x is the smallest element: traverse 1 element
所以,你的算法的实际复杂度是:
T(n) = n/1 + n/2 + n/3 + ... + n/n =
= n(1 + 1/2 + 1/3 + .... + 1/n)
现在注意1 + 1/2 +.... + 1/n
是调和级数之和,它在O(logn)
这为您提供了O(nlogn)
的复杂度,而不是O(n^2)
的平均案例复杂度。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.