这是一段很长的文本,请耐心阅读。简而言之,问题是:是否有可行的原地基数排序算法?
初步
我有很多只使用字母“A”、“C”、“G”和“T”(是的,你猜对了:DNA)的小固定长度字符串需要排序。
目前,我使用std::sort
,它在所有常见的STL实现中使用introsort。这个方法效果还不错。然而,我相信基数排序完全适合我的问题集,并且在实践中应该表现得更好。
细节
我用一个非常天真的实现来测试了这个假设,对于相对较小的输入(大约为10,000),这是正确的(至少快两倍)。然而,当问题规模变得更大(N > 5,000,000)时,运行时间会极度下降。
原因很明显:基数排序需要复制整个数据(实际上在我的天真实现中不止一次)。这意味着我已经将~4 GiB放入了我的主内存中,这显然会影响性能。即使没有这个问题,我也无法承受使用这么多内存,因为问题的规模实际上会更大。
用例
Ideally,这个算法应该适用于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字符串的工作参考实现或至少是工作在原地的基数排序的好伪代码/描述?