是什么导致这两种插入排序实现之间的性能差异?

时间:2016-11-20 19:57:23

标签: python python-3.x

我有两个插入排序的实现。第一个是我的java教科书中的示例的转录(尽管有while循环而不是java for循环)

def insertion_sort(array):
    for i in range(1,len(array)):
        j = i
        while j > 0 and array[j] < array[j-1]:
            array[j],array[j-1] = array[j-1], array[j]
            j=j-1
    return array

第二个似乎是一个更加pythonic的实现。

def insertion_sort2(array):
    for i in range(1,len(array)):
        for j in range(i-1,-1,-1):
            if array[j+1] < array[j]:
                array[j+1],array[j] = array[j],array[j+1]
            else:
                break
    return array

在对两者进行计时之后,当列表已经排序或非常接近排序时,似乎第二个慢得多(慢3到4倍)。然而,随着反转次数的增加,第二次实施似乎占了上风(快10-15%)。我只是想知道是否有人可以解释造成这种差异的原因。谢谢!

编辑:这是我用来创建随机列表的功能。

def rand_list(length):
    return [random.randint(0,9*length) for _ in range(length)]

如果我想要一个部分排序的列表,我只需拨打list(range(length1)) + rand_list(length2)

至于计算运行时间,我使用了%timeit工具和两次datetime.now()次呼叫之间的差异。他们都给出了非常一致的结果。另外我只想强调一点,我每次都要创建一个新列表,而不是对已排序的列表进行排序。

1 个答案:

答案 0 :(得分:1)

我能够重现你的时间。对于随机数据或反向排序数据, insertion_sort2 比Python3.6上的 insertion_sort 快一点。但是,如您所述,对于已经排序的数据, insertion_sort2 的速度要慢三倍。

第一种情况(随机或排序数据略快)的原因是 range_iterator 对象比手动编写j=j-1和{更快地生成递减整数(使用内部C代码) {1}}这两个都是纯python步骤。因此,一旦for循环被预热,它比while循环等效运行得快一点。

第二种情况(排序数据慢得多)的原因是,当迭代次数为零时,while循环比等效for循环便宜。这是因为while循环没有设置成本。等效for循环仍然必须创建范围对象和 range_iterator 对象,然后才能找出没有迭代的对象。因此,while循环在空或几乎为空时击败for循环,因为它们避免了设置成本。