一个更为高效的实现方法是使用基数256,并逐字节对数字进行排序。这使得所有“获取字节”的工作都可以使用快速的位运算符来完成。不幸的是,似乎没有人在Python中实现了一种基数排序,它使用位运算符而不是对数。
所以,我自己动手并想出了这个“野兽”,它在小数组上的运行速度大约是有序数组的一半,并且在较大的数组上(例如len约为10,000,000)几乎与sorted的速度相当:
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)时间内得到排序后的数组。
但是,它不如可能快,并且在将其写成比其他所有基数排序更好的基数排序之前,我想使它更快。
对此运行告诉我,很多时间都花在了列表的方法上,这让我认为这个块:
for num in unsorted:
byte_at_offset = (num & byte_check) >> offset*8
buckets[byte_at_offset].append(num)
radix_sort_offset
占用了很多时间,如果你真正去看它,它完成了整个排序90%的工作。这段代码看起来可以使用numpy
来提高性能。不幸的是,我不太熟悉numpy
的更复杂的功能,所以无法找出解决方法。非常感谢您的帮助。
目前我正在使用itertools.chain.from_iterable
来展平buckets
,但如果有更快的建议,我相信这也会有所帮助。
最初,我有一个get_byte
函数,它返回一个数字的第n
个字节,但将代码内联使我获得了巨大的速度提升,所以我做了这个操作。
关于实现或者挤出更多性能的方式,任何其他评论也都会受到欢迎。我想听听您拥有的所有东西。
list.sort()
,而且我并不介意你的排序更快 :-) - Tim Peters