我有一组字符串,其中90%以“http://www。”
开头。我想按字母顺序对它们进行排序。
目前,我使用C ++ std :: sort()。但是std :: sort是基于比较的快速排序变体,并且比较具有长公共前缀的两个字符串效率不高。 然而(我认为)基数排序也行不通,因为由于长公共前缀,大多数字符串都被放在同一个桶中。
是否有比普通快排/基数排序更好的算法解决这个问题?
我有一组字符串,其中90%以“http://www。”
开头。我想按字母顺序对它们进行排序。
目前,我使用C ++ std :: sort()。但是std :: sort是基于比较的快速排序变体,并且比较具有长公共前缀的两个字符串效率不高。 然而(我认为)基数排序也行不通,因为由于长公共前缀,大多数字符串都被放在同一个桶中。
是否有比普通快排/基数排序更好的算法解决这个问题?
我认为,当你考虑到URL的平均长度时,尝试利用大约10个字符的常见前缀来处理时间甚至不值得。
只需尝试完全标准的排序。如果速度不够快,可以考虑并行化或分发完全标准的排序。这是一个简单直接的方法,一定会奏效。
最后我发现使用三路快速排序算法效果很好。我在www.larsson.dogma.net/qsufsort.c找到了这个算法。
这是我修改后的实现,接口类似于std::sort。在我的机器和数据集上,它比std::sort快约40%。
#include <iterator>
template<class RandIt> static inline void multiway_qsort(RandIt beg, RandIt end, size_t depth = 0, size_t step_len = 6) {
if(beg + 1 >= end) {
return;
}
struct { /* implement bounded comparing */
inline int operator() (
const typename std::iterator_traits<RandIt>::value_type& a,
const typename std::iterator_traits<RandIt>::value_type& b, size_t depth, size_t step_len) {
for(size_t i = 0; i < step_len; i++) {
if(a[depth + i] == b[depth + i] && a[depth + i] == 0) return 0;
if(a[depth + i] < b[depth + i]) return +1;
if(a[depth + i] > b[depth + i]) return -1;
}
return 0;
}
} bounded_cmp;
RandIt i = beg;
RandIt j = beg + std::distance(beg, end) / 2;
RandIt k = end - 1;
typename std::iterator_traits<RandIt>::value_type key = ( /* median of l,m,r */
bounded_cmp(*i, *j, depth, step_len) > 0 ?
(bounded_cmp(*i, *k, depth, step_len) > 0 ? (bounded_cmp(*j, *k, depth, step_len) > 0 ? *j : *k) : *i) :
(bounded_cmp(*i, *k, depth, step_len) < 0 ? (bounded_cmp(*j, *k, depth, step_len) < 0 ? *j : *k) : *i));
/* 3-way partition */
for(j = i; j <= k; ++j) {
switch(bounded_cmp(*j, key, depth, step_len)) {
case +1: std::iter_swap(i, j); ++i; break;
case -1: std::iter_swap(k, j); --k; --j; break;
}
}
++k;
if(beg + 1 < i) multiway_qsort(beg, i, depth, step_len); /* recursively sort [x > pivot] subset */
if(end + 1 > k) multiway_qsort(k, end, depth, step_len); /* recursively sort [x < pivot] subset */
/* recursively sort [x == pivot] subset with higher depth */
if(i < k && (*i)[depth] != 0) {
multiway_qsort(i, k, depth + step_len, step_len);
}
return;
}
http://www.google.com/xxxx
或http://yahoo.com/xxxx
。我们能否进一步改进它? - richselian这篇工程并行字符串排序的论文令人惊讶地在URL数据集上对大量单线程算法进行了基准测试(见第29页)。Rantala的多键快速排序变体带有缓存,效果最好;您可以在本文引用的代码库中测试multikey_cache8。
我已经在那篇论文中测试了数据集,如果它是任何指示,您将在前十个字符中看到不到一位熵,并在100个字符范围内区分前缀。进行100次基数排序会浪费缓存,而收益很小,例如对一百万个URL进行排序意味着您要在每个关键字中查找约20个可区分位,代价是约100个缓存未命中。
一般来说,基数排序在长字符串上的表现并不好,但Kärkkäinen和Rantala在 Engineering Radix Sort for Strings中描述的优化足以处理URL数据集。特别是,他们提前读取了8个字符,并将它们与字符串指针缓存;根据这些缓存值进行排序产生了足够的熵来解决缓存未命中问题。
对于更长的字符串,请尝试该存储库中的一些基于LCP的算法;根据我的经验,URL数据集处于高度优化的基数类型排序和在较长的字符串上渐进性更好的基于LCP的算法之间的临界点。