为什么我的基数排序python实现比快速排序慢?
我使用SciPy中的数组从Wikipedia重写了Python的原始基数排序算法,以提高性能并减少代码长度,我成功地实现了这一点。然后,我从识字编程中获得了经典的(内存中的、基于透视的)快速排序算法,并比较了它们的性能 我曾期望基数排序将超过某个阈值,而它没有。此外,我还发现了这样一个问题:“对于整数数组,基数排序比快速排序快吗?”。答案是 。。基准测试显示,对于大型阵列,MSB就地基数排序始终比快速排序快3倍以上 不幸的是,我无法重现结果;区别在于:(a)Erik选择Java而不是Python;(b)他使用MSB就地基数排序,而我只是在Python字典中填充存储桶 根据理论,基数排序应该比快速排序更快(线性);但显然,这在很大程度上取决于实施情况。那么我的错误在哪里呢 下面是比较两种算法的代码:为什么我的基数排序python实现比快速排序慢?,python,quicksort,radix-sort,Python,Quicksort,Radix Sort,我使用SciPy中的数组从Wikipedia重写了Python的原始基数排序算法,以提高性能并减少代码长度,我成功地实现了这一点。然后,我从识字编程中获得了经典的(内存中的、基于透视的)快速排序算法,并比较了它们的性能 我曾期望基数排序将超过某个阈值,而它没有。此外,我还发现了这样一个问题:“对于整数数组,基数排序比快速排序快吗?”。答案是 。。基准测试显示,对于大型阵列,MSB就地基数排序始终比快速排序快3倍以上 不幸的是,我无法重现结果;区别在于:(a)Erik选择Java而不是Python
from sys import argv
from time import clock
from pylab import array, vectorize
from pylab import absolute, log10, randint
from pylab import semilogy, grid, legend, title, show
###############################################################################
# radix sort
###############################################################################
def splitmerge0 (ls, digit): ## python (pure!)
seq = map (lambda n: ((n // 10 ** digit) % 10, n), ls)
buf = {0:[], 1:[], 2:[], 3:[], 4:[], 5:[], 6:[], 7:[], 8:[], 9:[]}
return reduce (lambda acc, key: acc.extend(buf[key]) or acc,
reduce (lambda _, (d,n): buf[d].append (n) or buf, seq, buf), [])
def splitmergeX (ls, digit): ## python & numpy
seq = array (vectorize (lambda n: ((n // 10 ** digit) % 10, n)) (ls)).T
buf = {0:[], 1:[], 2:[], 3:[], 4:[], 5:[], 6:[], 7:[], 8:[], 9:[]}
return array (reduce (lambda acc, key: acc.extend(buf[key]) or acc,
reduce (lambda _, (d,n): buf[d].append (n) or buf, seq, buf), []))
def radixsort (ls, fn = splitmergeX):
return reduce (fn, xrange (int (log10 (absolute (ls).max ()) + 1)), ls)
###############################################################################
# quick sort
###############################################################################
def partition (ls, start, end, pivot_index):
lower = start
upper = end - 1
pivot = ls[pivot_index]
ls[pivot_index] = ls[end]
while True:
while lower <= upper and ls[lower] < pivot: lower += 1
while lower <= upper and ls[upper] >= pivot: upper -= 1
if lower > upper: break
ls[lower], ls[upper] = ls[upper], ls[lower]
ls[end] = ls[lower]
ls[lower] = pivot
return lower
def qsort_range (ls, start, end):
if end - start + 1 < 32:
insertion_sort(ls, start, end)
else:
pivot_index = partition (ls, start, end, randint (start, end))
qsort_range (ls, start, pivot_index - 1)
qsort_range (ls, pivot_index + 1, end)
return ls
def insertion_sort (ls, start, end):
for idx in xrange (start, end + 1):
el = ls[idx]
for jdx in reversed (xrange(0, idx)):
if ls[jdx] <= el:
ls[jdx + 1] = el
break
ls[jdx + 1] = ls[jdx]
else:
ls[0] = el
return ls
def quicksort (ls):
return qsort_range (ls, 0, len (ls) - 1)
###############################################################################
if __name__ == "__main__":
###############################################################################
lower = int (argv [1]) ## requires: >= 2
upper = int (argv [2]) ## requires: >= 2
color = dict (enumerate (3*['r','g','b','c','m','k']))
rslbl = "radix sort"
qslbl = "quick sort"
for value in xrange (lower, upper):
#######################################################################
ls = randint (1, value, size=value)
t0 = clock ()
rs = radixsort (ls)
dt = clock () - t0
print "%06d -- t0:%0.6e, dt:%0.6e" % (value, t0, dt)
semilogy (value, dt, '%s.' % color[int (log10 (value))], label=rslbl)
#######################################################################
ls = randint (1, value, size=value)
t0 = clock ()
rs = quicksort (ls)
dt = clock () - t0
print "%06d -- t0:%0.6e, dt:%0.6e" % (value, t0, dt)
semilogy (value, dt, '%sx' % color[int (log10 (value))], label=qslbl)
grid ()
legend ((rslbl,qslbl), numpoints=3, shadow=True, prop={'size':'small'})
title ('radix & quick sort: #(integer) vs duration [s]')
show ()
###############################################################################
###############################################################################
从系统导入argv
从时间输入时钟
从pylab导入数组,矢量化
从pylab导入绝对值,log10,randint
从pylab导入符号学、网格、图例、标题、显示
###############################################################################
#基数排序
###############################################################################
def splitmerge0(ls,数字):##python(纯!)
seq=map(λn:((n//10**位)%10,n),ls)
buf={0:[],1:[],2:[],3:[],4:[],5:[],6:[],7:[],8:[],9:[]
返回减少(lambda acc,键:acc.extend(buf[键])或acc,
reduce(lambda(d,n):buf[d]。追加(n)或buf,seq,buf),[])
def splitmergeX(ls,数字):##python和numpy
seq=数组(矢量化(λn:((n//10**位)%10,n))(ls)).T
buf={0:[],1:[],2:[],3:[],4:[],5:[],6:[],7:[],8:[],9:[]
返回数组(reduce(lambda acc,key:acc.extend)(buf[key])或acc,
reduce(lambda(d,n):buf[d]。追加(n)或buf,seq,buf),[]))
def radixsort(ls,fn=splitmergeX):
返回减少(fn,xrange(int(log10(绝对值ls.max())+1)),ls)
###############################################################################
#快速排序
###############################################################################
def分区(ls、开始、结束、透视索引):
降低=开始
上限=末端-1
pivot=ls[pivot\u索引]
ls[pivot_index]=ls[end]
尽管如此:
而上下:休息
ls[下]、ls[上]=ls[上]、ls[下]
ls[结束]=ls[更低]
ls[下]=枢轴
返回较低
def qsort_范围(ls、开始、结束):
如果结束-开始+1<32:
插入\排序(ls、开始、结束)
其他:
pivot_index=分区(ls,start,end,randint(start,end))
qsort_范围(ls、start、pivot_索引-1)
Q排序范围(ls,枢轴索引+1,结束)
返回ls
def插入_排序(ls、开始、结束):
对于X范围内的idx(开始、结束+1):
el=ls[idx]
对于反向(xrange(0,idx))中的jdx:
如果ls[jdx]=2
上限=int(argv[2])##需要:>=2
颜色=dict(枚举(3*['r'、'g'、'b'、'c'、'm'、'k']))
rslbl=“基数排序”
qslbl=“快速排序”
对于X范围内的值(下限、上限):
#######################################################################
ls=randint(1,值,大小=值)
t0=时钟()
rs=半径排序(ls)
dt=时钟()-t0
打印“%06d--t0:%0.6e,dt:%0.6e”%(值,t0,dt)
符号学(值,dt,,%s.%color[int(log10(值))],标签=rslbl)
#######################################################################
ls=randint(1,值,大小=值)
t0=时钟()
rs=快速排序(ls)
dt=时钟()-t0
打印“%06d--t0:%0.6e,dt:%0.6e”%(值,t0,dt)
符号学(值,dt,'%sx'%color[int(log10(值))],标签=qslbl)
网格()
图例((rslbl,qslbl),numpoints=3,shadow=True,prop={'size':'small'})
标题(‘基数和快速排序:#(整数)与持续时间之比’)
显示()
###############################################################################
###############################################################################
这是比较大小为2到1250(水平轴)的整数数组的排序持续时间(以秒为单位)的结果;下曲线属于快速排序:
快速排序在功率变化时是平滑的(例如,在10、100或1000),但基数排序只是稍微跳跃,但在其他方面遵循与快速排序相同的路径,只是慢得多 您的数据表示非常昂贵。为什么要对存储桶使用hashmap?为什么要使用需要计算对数(=计算代价高昂)的base10表示法 避免lambda表达式之类的,我认为python还不能很好地优化它们
可以从为基准测试排序10字节字符串开始。而且:没有哈希映射和类似的昂贵数据结构。这里有几个问题 首先,正如在评论中指出的,您的数据集太小,理论上的复杂性无法克服代码中的开销 接下来,您的实现和所有那些不必要的函数调用以及复制列表都是非常低效的。以简单的过程方式编写代码几乎总是比函数式解决方案快(对于Python,其他语言在这里会有所不同)。您有一个快速排序的过程实现,因此,如果您以相同的样式编写基数排序,即使对于较小的列表,也可能会更快 最后,当您尝试使用大型列表时,可能会减少开销
from random import randint
from math import log10
from time import clock
from itertools import chain
def splitmerge0 (ls, digit): ## python (pure!)
seq = map (lambda n: ((n // 10 ** digit) % 10, n), ls)
buf = {0:[], 1:[], 2:[], 3:[], 4:[], 5:[], 6:[], 7:[], 8:[], 9:[]}
return reduce (lambda acc, key: acc.extend(buf[key]) or acc,
reduce (lambda _, (d,n): buf[d].append (n) or buf, seq, buf), [])
def splitmerge1 (ls, digit): ## python (readable!)
buf = [[] for i in range(10)]
divisor = 10 ** digit
for n in ls:
buf[(n//divisor)%10].append(n)
return chain(*buf)
def radixsort (ls, fn = splitmerge1):
return list(reduce (fn, xrange (int (log10 (max(abs(val) for val in ls)) + 1)), ls))
###############################################################################
# quick sort
###############################################################################
def partition (ls, start, end, pivot_index):
lower = start
upper = end - 1
pivot = ls[pivot_index]
ls[pivot_index] = ls[end]
while True:
while lower <= upper and ls[lower] < pivot: lower += 1
while lower <= upper and ls[upper] >= pivot: upper -= 1
if lower > upper: break
ls[lower], ls[upper] = ls[upper], ls[lower]
ls[end] = ls[lower]
ls[lower] = pivot
return lower
def qsort_range (ls, start, end):
if end - start + 1 < 32:
insertion_sort(ls, start, end)
else:
pivot_index = partition (ls, start, end, randint (start, end))
qsort_range (ls, start, pivot_index - 1)
qsort_range (ls, pivot_index + 1, end)
return ls
def insertion_sort (ls, start, end):
for idx in xrange (start, end + 1):
el = ls[idx]
for jdx in reversed (xrange(0, idx)):
if ls[jdx] <= el:
ls[jdx + 1] = el
break
ls[jdx + 1] = ls[jdx]
else:
ls[0] = el
return ls
def quicksort (ls):
return qsort_range (ls, 0, len (ls) - 1)
if __name__=='__main__':
for value in 1000, 10000, 100000, 1000000, 10000000:
ls = [randint (1, value) for _ in range(value)]
ls2 = list(ls)
last = -1
start = clock()
ls = radixsort(ls)
end = clock()
for i in ls:
assert last <= i
last = i
print("rs %d: %0.2fs" % (value, end-start))
tdiff = end-start
start = clock()
ls2 = quicksort(ls2)
end = clock()
last = -1
for i in ls2:
assert last <= i
last = i
print("qs %d: %0.2fs %0.2f%%" % (value, end-start, ((end-start)/tdiff*100)))
C:\temp>c:\python27\python radixsort.py
rs 1000: 0.00s
qs 1000: 0.00s 212.98%
rs 10000: 0.02s
qs 10000: 0.05s 291.28%
rs 100000: 0.19s
qs 100000: 0.58s 311.98%
rs 1000000: 2.47s
qs 1000000: 7.07s 286.33%
rs 10000000: 31.74s
qs 10000000: 86.04s 271.08%