簡體   English   中英

使用合並排序算法所需的最少比較次數?

[英]Minimum number of comparisons needed to use merge sort algorithm?

對於那些熟悉合並排序的人,我試圖找出合並兩個大小為n / 2的子數組所需的最小比較數,其中n是原始未排序數組中的項目數。

我知道算法的平均時間和最壞情況下的時間復雜度為O(nlogn),但我無法找出所需的確切最小比較數(以n表示)。

假設一旦完全遍歷列表之一,就可以合理地實施合並步驟的最小比較次數約為n/2 (順便說,仍為O(n) )。

例如,如果要合並兩個已經有效排序的列表,則將較大列表的第一個成員與較小列表進行n/2次比較,直到用盡為止; 那么無需復制即可復制較大的列表。

List 1    List 2    Merged List         Last Comparison
[1, 2, 3] [4, 5, 6] []                  N/A
[2, 3]    [4, 5, 6] [1]                 1 < 4
[3]       [4, 5, 6] [1, 2]              2 < 4
[]        [4, 5, 6] [1, 2, 3]           3 < 4
[]        [5, 6]    [1, 2, 3, 4]        N/A
[]        [6]       [1, 2, 3, 4, 5]     N/A
[]        []        [1, 2, 3, 4, 5, 6]  N/A

請注意,進行了3​​個比較,列表中有6個成員。

同樣,請注意,即使在最佳情況下,合並步驟仍有效地視為O(n) 合並排序算法的時間復雜度為O(n*lg(n))因為合並步驟在整個列表中為O(n) ,並且除法/合並發生在O(lg(n))個遞歸級別上。

這個答案給出了確切的結果,不僅給出了使用一些Landau符號書寫的漸近行為。

合並長度為mn的列表至少需要進行min( mn )比較。 原因是只有在完全處理完輸入列表之一之后,才可以停止比較元素,即,您至少需要遍歷兩個列表中較小的一個。 請注意,此比較次數僅對某些輸入就足夠了,因此在假定可能的輸入數據為最佳情況下,它是最小的。 對於最壞的情況,您會發現更高的數字,即n⌈lgn⌉−2⌈lgn⌉+1

n = 2 k為2的冪。 i是一個合並電平,其中0≤ <K。 在第i級,您執行2 ki − 1個合並,每個合並都需要2 i比較。 將這兩個數字相乘得出2 k − 1個比較,等於n / 2。 對合並的k個級別求和,將得到nk / 2 =( n lg n )/ 2個比較。

現在讓n小於2的冪。 k = lg n仍然表示合並級別的數量。 與2 k的情況相比,現在每個級別的比較少了一個。 因此,合並總數減少k ,導致2 k k / 2- k =(2 k / 2-1) k比較。 但是,如果刪除一個元素,導致n = 2 k − 2,則不會減少最上面的合並數,因為另一個列表已經是較短的列表。 這表明周圍的事情可能會變得更加困難。

因此,讓我們有一個演示程序,我們可以使用它來檢查以前的結果並計算其他值的比較次數:

mc = [0, 0]                                 # dynamic programming, cache previous results
k = 1                                       # ceil(lg n) in the loop
for n in range(2, 128):
    a = n // 2                              # split list near center
    b = n - a                               # compute length of other half list
    mc.append(mc[a] + mc[b] + min(a, b))    # need to sort these and then merge
    if (n & (n - 1)) == 0:                  # if n is a power of two
        assert mc[-1] == n*k/2              # check previous result
        k += 1                              # increment k = ceil(lg n)
print(', '.join(str(m) for m in mc))        # print sequence of comparison counts, starting at n = 0

這為您提供了以下順序:

0, 0, 1, 2, 4, 5, 7, 9, 12, 13, 15, 17, 20, 22, 25, 28, 32, 33, 35,
37, 40, 42, 45, 48, 52, 54, 57, 60, 64, 67, 71, 75, 80, 81, 83, 85,
88, 90, 93, 96, 100, 102, 105, 108, 112, 115, 119, 123, 128, 130, 133,
136, 140, 143, 147, 151, 156, 159, 163, 167, 172, 176, 181, 186, 192,
193, 195, 197, 200, 202, 205, 208, 212, 214, 217, 220, 224, 227, 231,
235, 240, 242, 245, 248, 252, 255, 259, 263, 268, 271, 275, 279, 284,
288, 293, 298, 304, 306, 309, 312, 316, 319, 323, 327, 332, 335, 339,
343, 348, 352, 357, 362, 368, 371, 375, 379, 384, 388, 393, 398, 404,
408, 413, 418, 424, 429, 435, 441

您可以在整數序列在線百科全書中查找該序列,該序列描述二進制擴展為0,...,n的1的總數 那里也有一些公式,但是它們要么不精確(涉及一些Landau符號術語),要么依賴於其他一些非平凡的序列,或者它們非常復雜。 我最喜歡的那個表達了我上面的程序所做的事情:

a(0)= 0,a(2n)= a(n)+ a(n-1)+ n,a(2n + 1)= 2a(n)+ n + 1。 -拉爾夫·斯蒂芬(Ralf Stephan),2003年9月13日

考慮到這些替代方案,我想我會堅持使用上述腳本來計算這些數字。 您可以刪除斷言以及與此相關的所有內容,並依靠a < b的事實,如果將其包含在更大的程序中,則也可以刪除輸出。 結果應如下所示:

mc = [0, 0]
for n in range(2, 1024):
    a = n // 2
    mc.append(mc[a] + mc[n - a] + a)

注意,例如對於n = 3,您只有兩個比較。 顯然,只有將兩個極值元素都與中值元素進行比較,這才行得通,這樣就不必再將極值元素與另一個元素進行比較。 這說明了為什么上述計算僅適用於最佳情況輸入。 最壞的情況是,您需要在某個點上相互計算最小和最大元素,從而導致由n lglg n − 2 lglg n + 1 +1公式計算得出的三個比較。

對於每次比較,您從兩個列表之一中排出一個元素。 因此,比較次數最多是兩個列表的長度之和。 正如Platinum展示的,如果到達一個數組的末尾而另一個數組中仍包含項,則可能會更少。

因此比較次數在n/2n

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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