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

如何提高python中的合并排序速度,python,algorithm,sorting,python-3.x,optimization,Python,Algorithm,Sorting,Python 3.x,Optimization,是的,这是一个家庭作业,但我最终用Java来完成它只是为了完成它,但现在python实现让我很困扰。我很确定我已经正确地实现了它,但它需要的时间比应该的要长。在300万次输入的情况下,它可以在25到32秒的时间内完成任何操作。我假设这与我拼接和添加列表的方式有关。我这里有源代码,如果你看到什么请告诉我 def merge_sort(seq): if len(seq) == 1: return seq left = merge_sort(seq[:len(seq)

是的,这是一个家庭作业,但我最终用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[:])
然后,对于递归调用,您可以使用一个helper函数,向该函数传递的不是子列表,而是索引到
x
。底层调用从
x
读取它们的值,并直接写入
result


要使其工作,seq数组的参数必须是对seq的引用,同时也是对helper数组的引用

您还可以添加一个参数来跟踪合并的方向,从而避免复制回步骤。例如,使用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);
}
void TopDownMergeSort(整数序列[],大小\u t n)
{
int*b;
if(n<2)
返回;
b=malloc(n*sizeof(seq[0]);
TopDownSplitMerge(序列,b,0,n,真);
免费(b);
}
void TopDownSplitMerge(整数a[],整数b[],大小L,大小ee,bool mtoa)
{
大小(r);;
如果((ee-ll)==1){//如果大小==1
if(!mtoa)//如果将a合并到b,则复制到b
b[ll]=a[ll];
返回;
}
rr=(ll+ee)>>1;//中点,右半部分的起点
TopDownSplitMerge(a、b、ll、rr、mtoa);
TopDownSplitMerge(a,b,rr,ee,!mtoa);
if(mtoa)//如果合并到a,则将b合并到a
合并(b、a、ll、rr、ee);
else//else将a合并到b
合并(a、b、ll、rr、ee);
}

另一种选择是使用自底向上的合并排序,它跳过递归步骤,只开始将偶数运行与奇数运行合并,初始运行大小为1。

为什么您认为25-32秒比应该花费的时间长得多?因为,我的教授说它太长了(我要求确保)。java实现大约需要5.5秒的时间。那么您可以选择语言了吗?在我看来,5-6倍于Java的改进并不是太糟糕。也就是说,你可以通过避免复制数据来加快速度。是的。我只想把时间缩短到10秒以下。这并不意味着什么,因为我通常只使用蟒蛇。但更多的是一种教育活动。因为在我工作的地方,我们在大多数项目中都使用python,所以最好知道如何优化任何涉及列表的内容。在这里,我还将尝试完全初始化结果列表,而不是在回家后使用append扩展它。昨天完成了作业,但这一直困扰着我。这似乎是个糟糕的主意。使用try-catch块,然后故意抛出
索引器来中断?“那太草率了!”麦克马海姆不,那太草率了。而且更快。这就是问题所在。使用@StefanPochmann的新代码,它可以以7.2~7.3秒的速度运行一百万个数字。(PC为宏碁Aspire E15)