Quicksort能够更快地排序更大的数字?

时间:2011-02-10 23:40:06

标签: python algorithm performance sorting quicksort

我正在搞乱Python试图练习我的排序算法,并找到了一些有趣的东西。

我有三个不同的数据:

  • x =要排序的数字数
  • y =数字所在的范围(所有随机生成的int)
  • z =排序所需的总时间

当:
x = 100000和
y =(0,100000)然后
z = 0.94182094911秒

当:
x = 100000和
y =(0,100)然后
z = 12.4218382537秒

当:
x = 100000和
y =(0,10)然后
z = 110.267447809秒

有什么想法吗?

代码:

import time
import random
import sys

#-----Function definitions

def quickSort(array): #random pivot location quicksort. uses extra memory.
    smaller = []
    greater = []
    if len(array) <= 1:
        return array
    pivotVal = array[random.randint(0, len(array)-1)]
    array.remove(pivotVal)
    for items in array:
        if items <= pivotVal:
            smaller.append(items)
        else:
            greater.append(items)
    return concat(quickSort(smaller), pivotVal, quickSort(greater))

def concat(before, pivot, after):
    new = []
    for items in before:
        new.append(items)
    new.append(pivot)
    for things in after:
        new.append(things)
    return new

#-----Variable definitions
list = []
iter = 0
sys.setrecursionlimit(20000)
start = time.clock() #start the clock

#-----Generate the list of numbers to sort
while(iter < 100000):
    list.append(random.randint(0,10))  #modify this to change sorting speed
    iter = iter + 1
timetogenerate = time.clock() - start #current timer - last timer snapshot

#-----Sort the list of numbers
list = quickSort(list)
timetosort = time.clock() - timetogenerate #current timer - last timer snapshot

#-----Write the list of numbers
file = open("C:\output.txt", 'w')
for items in list:
    file.write(str(items))
    file.write("\n")
file.close()
timetowrite = time.clock() - timetosort #current timer - last timer snapshot

#-----Print info
print "time to start: " + str(start)
print "time to generate: " + str(timetogenerate)
print "time to sort: " + str(timetosort)
print "time to write: " + str(timetowrite)
totaltime = timetogenerate + timetosort + start
print "total time: " + str(totaltime)

-------------------修订了新代码------------------------- ---

def quickSort(array): #random pivot location quicksort. uses extra memory.
    smaller = []
    greater = []
    equal = []
    if len(array) <= 1:
        return array
    pivotVal = array[random.randint(0, len(array)-1)]
    array.remove(pivotVal)
    equal.append(pivotVal)
    for items in array:
        if items < pivotVal:
            smaller.append(items)
        elif items > pivotVal:
            greater.append(items)
        else:
            equal.append(items)
    return concat(quickSort(smaller), equal, quickSort(greater))

def concat(before, equal, after):
    new = []
    for items in before:
        new.append(items)
    for items in equal:
        new.append(items)
    for items in after:
        new.append(items)
    return new

3 个答案:

答案 0 :(得分:34)

我认为这与枢轴的选择有关。根据分区步骤的工作方式,如果您有大量重复值,则在遇到许多重复项时,您的算法可能会退化为二次行为。例如,假设您正在尝试快速分配此流:

 [0 0 0 0 0 0 0 0 0 0 0 0 0]

如果您不小心如何进行分区步骤,则可能会很快退化。例如,假设您选择枢轴作为第0个,留下数组

 [0 0 0 0 0 0 0 0 0 0 0 0]

分区。您的算法可能会说较小的值是数组

 [0 0 0 0 0 0 0 0 0 0 0 0]

较大的值是数组

 []

这种情况导致quicksort退化为O(n 2 ),因为每次递归调用只会将输入的大小缩小一(即,通过拉出枢轴元素)

我注意到在您的代码中,您的分区步骤确实会这样做:

for items in array:
    if items <= pivotVal:
        smaller.append(items)
    else:
        greater.append(items)

给定一个流是同一个元素的一大堆副本,这将把它们全部放入一个数组中以递归排序。

当然,这似乎是一个荒谬的案例 - 这与减少数组中值的数量有何关联? - 但是当你对许多不同的元素进行排序时,它确实会出现。特别是,在几次分区之后,你可能会将所有相同的元素组合在一起,这将带你进入这种情况。

关于如何防止这种情况发生的讨论,关于如何在存在重复元素的情况下修改分区步骤以便快速工作,这是一个非常好的讨论by Bob Sedgewick and Jon Bentley。它与Dijkstra的Dutch national flag problem相关联,他们的解决方案非常聪明。

一个可行的选项是将输入分为三组 - less,equal和more。一旦你以这种方式打破了输入,你只需要对越来越少的组进行排序;相等的组已经排序。以上链接显示了如何在原地或多或少地执行此操作,但由于您已经使用了不合适的快速排序,因此修复应该很容易。这是我的尝试:

for items in array:
    if items < pivotVal:
        smaller.append(items)
    elif items == pivotVal:
        equal.append(items)
    else:
        greater.append(items)

我生命中从未写过一行Python,BTW,所以这可能是完全非法的语法。但我希望这个想法很明确! : - )

答案 1 :(得分:2)

我们知道的事情:

  1. 快速排序无序数组的时间复杂度为O(n*logn)
  2. 如果数组已经排序,则会降级为O(n^2)
  3. 前两个语句不是离散的,即数组离排序越近,快速排序到O(n^2)的时间复杂度就越接近,而当我们改变它时,复杂性接近O(n*logn) < / LI>

    现在,让我们来看看你的实验:

    • 在所有三种情况下,您使用了相同数量的元素。因此,您命名为n的{​​{1}}始终为100000。
    • 在您的第一个实验中,您使用了0到100000之间的数字,因此理想情况下,使用完美的随机数生成器,您会在相对无序的列表中获得大多数不同的数字,从而符合x复杂性情况。
    • 在第三个实验中,您在100000个元素的大列表中使用了0到10之间的数字。这意味着列表中有很多重复项,使得它比排序列表更接近于第一个实验。因此,在这种情况下,时间复杂度更接近O(n*logn)

    使用相同足够大的O(n^2),您可以说n,您实际已经通过实验确认了这一点。

答案 2 :(得分:1)

快速排序算法有一个已知的弱点 - 当数据大部分被排序时它会变慢。当你有0到10之间的100000时,它们将比0到100000范围内的100000个数字更接近“大部分排序”。