簡體   English   中英

如何提高 python 中的合並排序速度

[英]How to improve Merge Sort speed in python

是的,這是作業,但我最終在 Java 完成它只是為了完成它,但現在 python 實現讓我感到困擾。 我很確定我已經正確地實施了它,但它花費的時間比它應該的要長。 在 300 萬個輸入上,它需要 25 到 32 秒的時間。 我假設它與我拼接和附加到列表的方式有關。 我這里有源代碼,如果你看到什么請告訴我。

def merge_sort(seq):
    if len(seq) == 1:
        return seq
    left = merge_sort(seq[:len(seq) // 2])
    right = merge_sort(seq[len(seq) // 2:])

    return merge(left, right)


def merge(left, right):
    result = []
    left_count = 0
    right_count = 0
    while len(left) > left_count and len(right) > right_count:
        if left[left_count] > right[right_count]:
            result.append(right[right_count])
            right_count += 1
        else:
            result.append(left[left_count])
            left_count += 1

    while len(left) > left_count:
        result.append(left[left_count])
        left_count += 1

    while len(right) > right_count:
        steps += 1
        result.append(right[right_count])
        right_count += 1

    return result

我認為你是對的。 切片會創建一個包含切片元素的新列表。 這必然是一項昂貴的操作。

在Java中,沒有一般的切片功能。 但是,如果您使用List.subList將返回原始視圖而不是副本,我認為會更快。 就地陣列操作,會更快。

運用

while True:

代替

while len(left) > left_count and len(right) > right_count:

讓我大約快40-45%:

def merge(left, right):
    result = []
    left_count = 0
    right_count = 0
    try:
        while True:
            if left[left_count] > right[right_count]:
                result.append(right[right_count])
                right_count += 1
            else:
                result.append(left[left_count])
                left_count += 1
    except:
        return result + left[left_count:] + right[right_count:]

最后一行似乎沒有讓它更快,但我更喜歡它。

來自Rishav Kundu鏈接的先前帖子:


您可以在對mergesort的頂級調用中初始化整個結果列表:

result = [0]*len(x)   # replace 0 with a suitable default element if necessary. 
                      # or just copy x (result = x[:])

然后對於遞歸調用,您可以使用輔助函數,而不是將子列表傳遞給x ,而將索引傳遞給x 底層調用從x讀取它們的值並直接寫入result


為此,seq數組的參數需要是對seq和輔助數組的引用。

您還可以添加參數以跟蹤要合並的方向,以避免復制步驟。 C示例使用mtoa標志,表示從b合並到a(如果為false,則表示將a合並到b)。 在我的系統上,Intel 2600K 3.4ghz,此代碼在大約0.36秒內分配400萬個偽隨機32位無符號整數,在大約1.6秒內分類1600萬。

void TopDownMergeSort(int seq[], size_t n)
{
int * b;
    if(n < 2)
        return;
    b = malloc(n * sizeof(seq[0]));
    TopDownSplitMerge(seq, b, 0, n, true);
    free(b);
}

void TopDownSplitMerge(int a[], int b[], size_t ll, size_t ee, bool mtoa)
{
size_t rr;
    if ((ee - ll) == 1){                    // if size == 1
        if(!mtoa)                           //  copy to b if merging a to b
            b[ll] = a[ll];
        return;
    }
    rr = (ll + ee)>>1;                      // midpoint, start of right half
    TopDownSplitMerge(a, b, ll, rr, !mtoa);
    TopDownSplitMerge(a, b, rr, ee, !mtoa);
    if(mtoa)                                // if merging to a, merge b to a
        Merge(b, a, ll, rr, ee);
    else                                    // else merge a to b
        Merge(a, b, ll, rr, ee);
}

另一種選擇是使用自下而上的合並排序,它會跳過遞歸步驟,只是開始合並甚至是奇數運行的運行,初始運行大小為1。

This is 2.7x faster than the op's code and 2x faster than @Stefan Pochmann 's

def merge(left, right):
    result = []
    left_count = 0
    right_count = 0

    try:
        while True:
            result.append(right[right_count] if left[left_count] > right[right_count] else left[left_count])
            right_count += left[left_count] > right[right_count]
            left_count += left[left_count] <= right[right_count]
    except:
        return result + (left[left_count:] if len(left) > left_count else right[right_count:])

暫無
暫無

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

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