具有极大不同运行时间的MergeSort的两种实现方式

时间:2016-11-06 03:17:38

标签: python performance sorting mergesort

我是初学者,学习有趣的算法,我正在尝试在Python中实现合并排序。

以下是我的实施。当我提供100000个列表时它非常慢。

def mergesortedlist(L, R):
    LR = [0]*(len(L)+len(R))  ##merged output (LR combined)
    i= 0     ##counter for left side
    j= 0      ##counter for ride side
    k = 0
    while i <= len(L) and j <= len(R):

        if i == len(L): 
            LR[k:]= R[j:]
            return LR

        elif j == len(R): 
            LR[k:] = L[i:]
            return LR
        elif L[i] < R[j]: 
            LR[k]= L[i]
            i+=1
            k+=1
        else: ##left side is bigger than right side
            LR[k]=R[j]
            j+=1
            k+=1   

def mergesort(N):  

    if len(N) <= 1: 
        return N
    else:
        sub1 = N[0:round(len(N)/2)]  
        sub2 = N[round(len(N)/2):]
        return mergesortedlist(mergesort(sub1), mergesort(sub2))

以下是我在本网站(http://interactivepython.org/courselib/static/pythonds/SortSearch/TheMergeSort.html

上在线找到的实施方案
def mergeSort(alist):
    print("Splitting ",alist)
    if len(alist)>1:
        mid = len(alist)//2
        lefthalf = alist[:mid]
        righthalf = alist[mid:]

        mergeSort(lefthalf)
        mergeSort(righthalf)

        i=0
        j=0
        k=0
        while i < len(lefthalf) and j < len(righthalf):
            if lefthalf[i] < righthalf[j]:
                alist[k]=lefthalf[i]
                i=i+1
            else:
                alist[k]=righthalf[j]
                j=j+1
            k=k+1

        while i < len(lefthalf):
            alist[k]=lefthalf[i]
            i=i+1
            k=k+1

        while j < len(righthalf):
            alist[k]=righthalf[j]
            j=j+1
            k=k+1
    print("Merging ",alist)

alist = [54,26,93,17,77,31,44,55,20]
mergeSort(alist)
print(alist)

速度非常快。

据我所知,我的实现也是O(Nlog(N)),为什么我的速度慢得多?

感谢您的帮助。

干杯!

2 个答案:

答案 0 :(得分:0)

我没有看到巨大的性能差异:对于1,000,000个随机浮点数,7.3s对5.4s。但你纠正第二个更快。

以下是如何对其进行分析以了解花费时间的事情。

python -m cProfile -s time mergesort.py

你的输出:

999999    8.517    0.000   11.088    0.000 mergesort.py:2(mergesortedlist)
1999999/1    2.735    0.000   14.151   14.151 mergesort.py:25(mergesort)
84184856/84184834    2.725    0.000    2.725    0.000 {len}
1999998    0.174    0.000    0.174    0.000 {round}

第二个:

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
1999999/1    7.377    0.000    8.721    8.721 mergesort2.py:1(mergeSort)
40499148/40499126    1.344    0.000    1.344    0.000 {len}

你看到你的花费更多时间来调用len()和round()。

当你的数组返回数组时,第二个操作是通过引用传递的数组。复制也可能需要一些时间。由于您是初学者,因此查看difference

是有意义的

答案 1 :(得分:0)

我的2美分。 在我的测试中,round()len()函数都不会显着影响运行时间。 我认为问题如下:在每个mergesortedlist()调用中,您创建一个新列表。 我用IPython的%%timeit

测试了这段代码
L = N[0:round(len(N)/2)]  
R = N[round(len(N)/2):]
LR = [0]*(len(L)+len(R))

2,000,000个元素 - &gt; 10个循环,最佳3:34.4 ms每循环
1,000,000个元素 - &gt; 100循环,最佳3:每循环17.2毫秒
500,000个元素 - &gt; 100循环,最佳3:每循环8.3毫秒
250,000个元素 - &gt; 100循环,最佳3:3.49毫秒每循环
125,000个元素 - &gt; 1000循环,最佳3:每循环1.25毫秒
62,500个元素 - &gt; 1000循环,最佳3:每循环526μs
所以...

请记住,Mergesort的最坏情况是O(N * logN)。 (这个事实拒绝了我们将第一个排序函数应用于随机数而第二个排序到排序数字的情况。)