【发布时间】:2010-10-02 13:30:43
【问题描述】:
这是一个长文本。请多多包涵。归结起来,问题是:是否有可行的就地基数排序算法?
初步
我有大量的固定长度的字符串,它们只使用字母“A”、“C”、“G”和“T”(是的,你已经猜到了它:DNA) 我要排序。
目前,我使用std::sort,它在STL 的所有常见实现中使用introsort。这工作得很好。但是,我确信 radix sort 非常适合我的问题集,并且在实践中应该大大更好地工作。
详情
我已经用一个非常简单的实现测试了这个假设,并且对于相对较小的输入(大约 10,000 个)这是正确的(嗯,至少快两倍以上)。但是,当问题规模变大(N > 5,000,000)时,运行时间会大幅下降。
原因很明显:基数排序需要复制整个数据(实际上在我的幼稚实现中不止一次)。这意味着我已将 ~ 4 GiB 放入我的主内存中,这显然会影响性能。即使没有,我也不能使用这么多内存,因为问题实际上变得更大了。
用例
理想情况下,此算法应适用于 2 到 100 之间的任何字符串长度,适用于 DNA 和 DNA5(允许额外的通配符“N”),甚至是带有IUPAC ambiguity codes 的 DNA(导致 16不同的值)。但是,我意识到无法涵盖所有这些情况,因此我对获得的任何速度改进感到满意。代码可以动态决定分派到哪个算法。
研究
不幸的是,Wikipedia article on radix sort 没用。关于就地变体的部分完全是垃圾。 NIST-DADS section on radix sort 几乎不存在。有一篇听起来很有前途的论文Efficient Adaptive In-Place Radix Sorting 描述了算法“MSL”。不幸的是,这篇论文也令人失望。
特别是有以下几点。
首先,该算法包含几个错误并且有很多无法解释的地方。特别是,它没有详细说明递归调用(我只是假设它增加或减少了一些指针来计算当前的移位和掩码值)。此外,它使用函数dest_group 和dest_address 而不给出定义。我看不出如何有效地实现这些(也就是说,在 O(1) 中;至少 dest_address 不是微不足道的)。
最后但同样重要的是,该算法通过将数组索引与输入数组中的元素交换来实现就地性。这显然只适用于数值数组。我需要在字符串上使用它。当然,我可以只是搞砸强类型并继续假设内存可以容忍我在不属于它的地方存储一个索引。但这只有在我可以将字符串压缩到 32 位内存(假设为 32 位整数)时才有效。那只有 16 个字符(让我们暂时忽略 16 > log(5,000,000))。
其中一位作者的另一篇论文根本没有给出准确的描述,但它认为 MSL 的运行时间是亚线性的,这完全是错误的。
回顾:是否有希望找到一个有效的参考实现,或者至少有一个很好的伪代码/描述一个适用于 DNA 字符串的就地基数排序?
【问题讨论】:
-
这是一个写得很好的问题。
-
固定长度的小字符串有多小?
-
@EvilTeach:我已经添加了用例。
-
@Stephan:这一切都很好。但是在复制/缓存未命中的情况下,我只是得到了延迟。在记忆的情况下,我达到了物理极限。这简直是不可谈判的。所有这些将部分数据存储在磁盘上的花哨技术肯定比当前的快速排序解决方案慢。
-
另一方面,(cont') dsimcha 的解决方案对于某些输入而言绝对比快速排序快。移动次数可能很高,缓存局部性很小,但在现实世界中,它仍然很好。我还稍微调整了解决方案,以减少我需要执行的交换次数。
标签: algorithm language-agnostic sorting radix-sort in-place