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