我一直非常沮丧,python radix的许多实现都在Web上进行排序。推基数排序(和python)到其极限
它们始终使用10的基数,并通过除以10的幂或取数字的log10来获得它们迭代的数字的数字。这是非常低效的,因为与比特移位相比,log10并不是特别快速的操作,比位移快了近100倍!
更高效的实现使用256的基数并逐字节地对数字进行排序。这允许使用可靠的快速位操作符来完成所有'字节获取'。不幸的是,似乎绝对没有人在Python中使用位运算符而不是对数来实现基数排序。
于是,我带着问题到我自己的手,并用此兽,它在上排序小数组大约一半的速度运行,那么快运行在较大的想出了(如len
周围10,000,000):
import itertools
def radix_sort(unsorted):
"Fast implementation of radix sort for any size num."
maximum, minimum = max(unsorted), min(unsorted)
max_bits = maximum.bit_length()
highest_byte = max_bits // 8 if max_bits % 8 == 0 else (max_bits // 8) + 1
min_bits = minimum.bit_length()
lowest_byte = min_bits // 8 if min_bits % 8 == 0 else (min_bits // 8) + 1
sorted_list = unsorted
for offset in xrange(lowest_byte, highest_byte):
sorted_list = radix_sort_offset(sorted_list, offset)
return sorted_list
def radix_sort_offset(unsorted, offset):
"Helper function for radix sort, sorts each offset."
byte_check = (0xFF << offset*8)
buckets = [[] for _ in xrange(256)]
for num in unsorted:
byte_at_offset = (num & byte_check) >> offset*8
buckets[byte_at_offset].append(num)
return list(itertools.chain.from_iterable(buckets))
这个版本的基数排序的工作原理是找出它必须排序的字节(如果你只传递256以下的整数,它将只排序一个字节等),然后通过转储排序LSB中的每个字节装入桶中,然后将桶连在一起。对需要排序的每个字节重复此操作,并且您在O(n)时间内有很好的排序数组。
但是,它并没有像它可能的那么快,而且我希望能够把它写得更快,然后再把它写成更好的基数排序,而不是像所有其他基数排序那样。
运行cProfile
这个告诉我,很多的时间被消耗在append
方法列表,这让我觉得此块:
for num in unsorted:
byte_at_offset = (num & byte_check) >> offset*8
buckets[byte_at_offset].append(num)
在radix_sort_offset
是吃了大量的时间。这也是一个阻碍,如果你真的看看它,整个工作的90%。这段代码看起来好像可能是numpy
-ized,我认为这会带来相当的性能提升。不幸的是,我对numpy
的更复杂的功能并不擅长,所以一直无法弄清楚。帮助将非常感激。
我目前使用itertools.chain.from_iterable
来平整buckets
,但如果有人有更快的建议,我相信它也会有帮助。
最初,我有一个get_byte
函数返回一个数字的第012个字节,但内联代码给了我一个巨大的速度提升,所以我做到了。
其他有关实施或其他方面的更多性能的评论也值得赞赏。我想听到任何事情和你所拥有的一切。
好东西。这导致了非常强大的加速,并允许这种基数排序在一个列表10,000,000长的基础上以4096的基数排序,但是这确实使得它在短列表上令人尴尬地执行。编辑:刚才意识到你是写timsort的人。先生,我的帽子是给你的。 – reem
赫 - 我敢打赌,你在这个列表中没有任何负数的整数;-)基数排序非常好,但是当你超越非负数整数时,这个小小的摆弄就会变得更加棘手。顺便说一句,我写了Python的'list.sort()',我并不感到冒犯你的速度更快:-) –