Python中的Timsort算法

Timsort算法被认为是一种混合排序算法,因为它采用了插入排序和合并排序的两种方法的最佳组合。Timsort对于Python社区来说非常重要,因为它是由Tim Peters在2002年创建的,用于作为Python语言的标准排序算法。

Timsort的主要特点是它利用了存在于大多数真实数据集中的已经排序的元素。这些被称为自然运行。然后,算法遍历列表,将元素收集到run中,并将它们合并到一个排序的列表中。

在Python中实现Timsort

在本文中,您将创建一个简单的Python实现,它演示了Timsort算法的所有部分。如果您感兴趣,还可以查看Timsort的原始C实现。

实现Timsort的第一步是修改insertion_sort()的实现:


这个修改后的实现添加了两个参数,左参数和右参数表示数组的哪个部分应该排序。这允许Timsort算法对数组的一部分进行排序。修改函数而不是创建一个新的函数意味着它可以被插入排序和Timsort重用。

现在看看Timsort的实现:


尽管实现比以前的算法要复杂一些,但是我们可以通过以下方式快速总结一下:

第6行和第7行创建或运行数组的小片,并使用插入排序对它们进行排序。您以前了解到插入排序在小列表中是快速的,Timsort利用了这一点。Timsort使用insertion_sort()中新引入的左参数和右参数对列表进行排序,而不需要创建新的数组(如merge sort和quicksort)第11行合并了这些较小的运行,最初每个运行的大小为32。在每次迭代中,运行的大小都会增加一倍,并且算法会继续合并这些较大的运行,直到只剩下一个排序的运行。结合上述两种情况,可以为min_run提供几个选项。本教程中的实现使用min_run = 32作为一种可能性。

测量Timsort的大O复杂度

平均来说,Timsort的复杂度是O(n log2n),就像合并排序和快速排序一样。对数部分来自于将执行每个线性合并操作的运行大小加倍。

然而,Timsort在已经排序或接近排序的列表上执行得非常好,这导致了O(n)的最佳情况。在这种情况下,Timsort明显优于合并排序,并与快速排序的最佳情况相匹配。但是Timsort最坏的情况也是O(n log2n),它超过了快速排序的O(n2)。

计时您的Timsort实现

您可以使用run_sorting_algorithm()来查看Timsort是如何对包含10,000个元素的数组进行排序的:

现在执行脚本,得到timsort的执行时间:


现在执行脚本,得到timsort的执行时间:

Shell
$ python sorting.py
Algorithm: timsort. Minimum execution time: 0.5121690789999998

如果你现在执行脚本,那么所有的算法都会运行并输出相应的执行时间:

在0.51秒,这个Timsort实现比merge sort快了整整0.1秒,即17%,尽管它不匹配快速排序的0.11。它也比插入排序快了11000个百分点!

现在尝试使用这四种算法对已经排序的列表进行排序,看看会发生什么。你可以修改你的_main__部分如下:

如果你现在执行脚本,那么所有的算法都会运行并输出相应的执行时间:


如果你现在执行脚本,那么所有的算法都会运行并输出相应的执行时间:

Shell
Algorithm: insertion_sort. Minimum execution time: 53.5485634999991
Algorithm: merge_sort. Minimum execution time: 0.372304601
Algorithm: quicksort. Minimum execution time: 0.24626494199999982
Algorithm: timsort. Minimum execution time: 0.23350277099999994

这一次,Timsort的速度比归并排序快了37%,比快速排序快了5%,充分展示了它利用已经排序的运行的能力。

注意Timsort如何从两种算法中获益,这两种算法在单独使用时要慢得多。Timsort的天才之处在于将这些算法组合在一起,并发挥它们的优势来获得令人印象深刻的结果。


分析Timsort的优缺点

Timsort的主要缺点是它的复杂性。尽管实现了原始算法的一个非常简化的版本,但它仍然需要更多的代码,因为它同时依赖于insertion_sort()和merge()。

Timsort的优点之一是,无论输入数组的结构如何,它都可以在O(n log2n)中执行。与快速排序相比,快速排序可以降低到O(n2)。Timsort对于小数组也非常快,因为算法变成了单个插入排序。

在实际应用中,对已经有一些预先存在的顺序的数组进行排序是很常见的,Timsort是一个很好的选择。它的适应性使其成为对任意长度的数组进行排序的最佳选择。