- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我对网络上的许多 python radix sort 实现感到非常沮丧。
他们始终使用 10 的基数,并通过除以 10 的幂或取数字的 log10 来获得他们迭代的数字的数字。这是非常低效的,因为与位移位相比,log10 并不是一个特别快的操作,位移位快了将近 100 倍!
一个更有效的实现使用基数 256 并逐字节对数字进行排序。这允许使用快得离谱的位运算符完成所有“字节获取”。不幸的是,似乎绝对没有人在 python 中实现了使用位运算符而不是对数的基数排序。
所以,我自己动手并想出了这个野兽,它在小型数组上的运行速度大约是排序的一半,而在较大的数组上运行速度几乎一样快(例如 len
around 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
方法上,这让我认为这个 block :
for num in unsorted:
byte_at_offset = (num & byte_check) >> offset*8
buckets[byte_at_offset].append(num)
在 radix_sort_offset
中消耗了很多时间。这也是一个 block ,如果你仔细观察它,它会为整个排序完成 90% 的工作。这段代码看起来可能是 numpy
化的,我认为这会带来相当大的性能提升。不幸的是,我不太熟悉 numpy
的更复杂的功能,所以没能弄明白。非常感谢您的帮助。
我目前正在使用 itertools.chain.from_iterable
来展平 buckets
,但如果有人有更快的建议,我相信它也会有所帮助。
最初,我有一个返回数字的第 n
字节的 get_byte
函数,但是内联代码给了我巨大的速度提升,所以我做到了。
对于实现或提高性能的方法的任何其他评论也表示赞赏。我想听听你的一切。
最佳答案
你已经意识到了
for num in unsorted:
byte_at_offset = (num & byte_check) >> offset*8
buckets[byte_at_offset].append(num)
是大部分时间去的地方 - 很好 ;-)
有两个标准技巧可以加快这种事情的速度,都与将不变量移出循环有关:</p>
bucketappender = [bucket.append for bucket in buckets]
。保存每次迭代的方法查找。将它们结合起来,循环看起来像:
for num in unsorted:
bucketappender[(num & byte_check) >> ofs8](num)
将其折叠为一个语句还会在每次迭代时保存一对本地 vrbl 存储/获取操作码。
但是,在更高的层次上,加速基数排序的标准方法是使用更大的基数。 256有什么神奇之处?没什么,除此之外它便于移位。但 512、1024、2048 也是如此……这是经典的时间/空间权衡。
PS:对于非常长的数字,
(num >> offset*8) & 0xff
会跑得更快。那是因为您的 num & byte_check
花费的时间与 log(num)
成正比 - 它通常必须创建一个与 num
一样大的整数。
关于python - 将基数排序(和 python)推向极限,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20207791/
正如标题所说,我需要制作一个函数,在二进制补码中的 2 个碱基、DEC 和 HEX 之间进行转换。该值使用的位数从一开始就已知。 在深入研究之后,我发现了以下算法: 给定一个 DEC 中的数字。 获取
我的用户文档具有以下格式: { userId: "", userAttributes: [ "", "", ... ""
根据这个: Selectivity is the value between 0 and 1, and it is the fraction of rows returned after applyi
这个词有它 FillChar 是用相同值的字节填充内存补丁的最快方法(不是零,因为有 ZeroMemory),但是是否有等效于用相同的序列填充内存(四字节)整数或基数?像 FillInt 或 Fill
我正在努力寻找建模 1 : 0,1 关系的最佳方法(“可能有一个”或“最多有一个”)。我相信这被称为 Z 基数。 例如,假设我有两个类 Widget和 WidgetTest .并非所有 Widget
我使用parseInt找到了一个片段;它用于获取窗口高度。 这是代码: parseInt($(window).height(), 20); 我很困惑为什么使用 20 作为第二个参数。为什么不是 10
要将十进制数转换为基数 2,我使用: int base2 = 10; Convert.ToString(base2, 2); 输出:1010 但是我怎么能做相反的事情呢?即: 输入:1010输出:10
这是一张真实 table 的再现。假设我有这段代码: CREATE TABLE `testTable` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
由于十六进制(基数 16)使用 0-9A-F,并且(我在这里假设)基数 17 使用 0-9A-G,依此类推。什么符号用过一次0-9A-Z都用完了。 最佳答案 你的问题没有标准答案。 “Base 36”
我正在寻找支持 radix 的浏览器列表Number.toString() 中的参数在 JavaScript 中。全部执行toString ,但我找不到他们是否都支持 radix toString 的
这个问题已经有答案了: What is the radix parameter in Java, and how does it work? (6 个回答) 已关闭 5 年前。 public clas
为什么 (73).toString(36) 返回 21 而 (0.73).toString(36) 返回 0。 qa2voha2volfpsnhmyhqia4i 而不是 0.21? 最佳答案 这是因为
我目前正在研究数据库,我看到 degree 和 cardinality 用作相同的术语,或在某些其他学位定义为否。关系中涉及的实体的数量,并进一步分类为一元、二元和三元。 某些放置度数定义为关系类型的
UML(统一建模语言)中的运算符*和运算符0..*有什么区别? 我看到了这两个基数运算符,但是现在我不必使用哪个基数运算符了。 最佳答案 符号“*”是“0 .. *”的快捷方式。在这种情况下使用的正确
我有位于目录“someApp”中的 Angular 应用程序。网址是 http://example-domain/someApp/#/对于一些带有路径的状态 url 是:http://example-
我想一劳永逸地知道如何编写 UML 基数,因为我经常不得不讨论它们(因此非常欢迎证据和来源:) 如果我想解释一下 Mother可以有几个Child任但是 Child有一个而且只有一个 Mother ,
进行字符算术时,规则是以 10 为基数还是以 8 为基数进行计算?我的书上说'A' = 101(基数为8)或65(基数为10),但是当我将基数为8的字符值插入到我的书给出的关于说明这一点的示例中时,我
该程序是将 4 进制数转换为 2 进制数,并且应该就地完成 #include #include void shiftr(char num[],int i) { memmove(num+i,n
这个问题已经有答案了: JavaScript parseInt is giving me wrong number, what I'm doing wrong? [duplicate] (1 个回答)
我遇到了一个小错误,它似乎表明当您传入图像数据作为其源时,在图像完全加载之前调用了 onload 函数。 这是 HTML 这是 JavaScript: var can
我是一名优秀的程序员,十分优秀!