You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中快速排序性能对比:随机枢轴vs固定枢轴

问题:随机枢轴快排比固定最后元素枢轴快100倍?

我基于CLR实现了一个简单的快速排序,采用随机选择枢轴的方式:

def qsr(l, s, e):
    def qsrpartition(l, s, e):
        pivotindex=random.randrange(s,e+1)
        l[e], l[pivotindex] = l[pivotindex], l[e]
        p = l[e]
        i = s - 1
        for j in range(s, e):
            if l[j] <= p:
                i = i+1
                l[i], l[j] = l[j], l[i]
        l[i+1], l[e] = l[e], l[i+1]
        return i+1
        
    if s < e:
        q = qsrpartition(l, s, e)
        qsr(l, s, q-1)
        qsr(l, q+1, e)    

我还有一个采用固定枢轴选择的版本(注释掉qsrpartition的前两行)。如果在随机数组上运行这两个版本,上述随机版本比总是选择最后一个元素作为枢轴的版本快100倍(1秒vs10秒)。

测试代码及结果:

li = random.choices(range(5000), k=5000)
li2 = random.choices(range(5000), k=5000)
r1 = timeit.timeit(lambda:qs(li, 0, len(li)-1),number=10)
r2 = timeit.timeit(lambda:qsr(li2, 0, len(li2)-1),number=10)
print(r1, r2)

11.10 0.08

上述结果在多次运行、不同数组长度、有无放回采样、函数运行顺序等场景下均具有统计显著性。按道理,对于随机数组而言,枢轴的选择应该无关紧要,因为最后一个元素应该和其他元素一样合适。我实在搞不懂这是为什么,求技术解答。


解答

核心问题出在你测试固定枢轴版本时,数组已经被排序过一次了!

看你的测试代码:timeit会重复执行lambda:qs(li, 0, len(li)-1)共10次。第一次执行后,li已经被完全排序了,剩下的9次都是在对已排序数组进行固定最后元素作为枢轴的快排——这会触发快排的最坏情况,时间复杂度退化为O(n²)。

而随机枢轴版本的li2呢?每次执行qsr(li2, ...)时,虽然第一次也会排序数组,但随机枢轴的快排在已排序数组上依然能保持O(n log n)的时间复杂度,因为随机选枢轴会打破已排序数组的最坏情况结构。

验证这个结论很简单:把测试代码改成每次执行前都重新生成随机数组,比如:

r1 = timeit.timeit(lambda: qs(random.choices(range(5000), k=5000), 0, 4999), number=10)
r2 = timeit.timeit(lambda: qsr(random.choices(range(5000), k=5000), 0, 4999), number=10)

这样两个版本的执行时间就会基本一致,因为每次都是对全新的随机数组排序,固定最后元素作为枢轴的快排也能达到O(n log n)的平均复杂度,和随机枢轴版本性能接近。

总结一下:你之前的测试用例犯了一个常见错误——复用了已经被排序的数组,导致固定枢轴版本连续执行最坏情况,而随机枢轴版本不受影响,所以出现了100倍的性能差距。


内容的提问来源于stack exchange,提问作者kdl_kdl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 08:45:25