簡體   English   中英

盡管 Big-O 相同,為什么這兩個函數的性能差異如此之大?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM