无论如何都要对这类数据进行优化排序?

时间:2012-06-19 02:20:57

标签: c algorithm sorting

我正在排序整数键的数组。

有关数据的信息:

  • 阵列长度为1176个元素
  • 钥匙在750 000至135 000 000之间;也可以是0
  • 有很多重复项,每个数组中只有48到100个不同的键,但不可能预测哪些值会超出整个范围
  • 有很多长的排序子序列,大多数数组包含33到80个排序后的子序列
  • 最小元素为0; 0的数量是可预测的并且在非常窄的范围内,每个阵列约150个

到目前为止我尝试了什么:

  1. stdlib.h qsort ;

    这很慢,现在我的函数在每次执行的排序上花费0.6秒,stdlib.h qsort是1.0s;这与std :: sort

  2. 具有相同的性能
  3. Timsort

    我试过这个:https://github.com/swenson/sort而且:http://code.google.com/p/timsort/source/browse/trunk/timSort.c?spec=svn17&r=17;两者都明显慢于stdlib qsort

  4. http://www.ucw.cz/libucw/ ;

    到目前为止,他们对快速排序和插入排序的组合对我的数据来说是最快的;我尝试了各种设置和枢轴作为中间元素(不是3的中位数)和插入排序从28元素子数组开始(默认情况下不是8)提供最佳性能

  5. shell sort ;

    简单的实现与本文的差距:http://en.wikipedia.org/wiki/Shellsort;虽然比stdlib qsort

  6. 慢,但还算不错

    我的想法是qsort做了大量的交换和废弃(即反向)排序的子序列,所以应该有一些方法通过利用数据的结构来改进它,不幸的是我到目前为止所有的尝试都失败了。
    如果你很好奇那是什么类型的数据,那些是在已经在前面板上排序的各种板上评估的扑克牌组(这是排序的子序列来自的地方)。

    该功能在C中。我使用Visual Studio 2010。 有什么想法吗?

    示例数据:http://pastebin.com/kKUdnU3N
    样本完全执行(1176种):https://dl.dropbox.com/u/86311885/out.zip

8 个答案:

答案 0 :(得分:7)

如果您首先通过数组对数字进行分组以消除重复项,该怎么办?每个数字都可以进入哈希表,其中数字是键,它出现的次数是值。因此,如果数组中的数字750 000出现57次,则哈希表将保持key = 750000;值= 57。然后,您可以按键对小得多的哈希表进行排序,键长度应少于100个元素。

使用此方法,您只需要通过数组一次,另一次通过更小的哈希表键列表。这应该避免大多数交换和比较。

答案 1 :(得分:5)

您可以查看此animation,我在此post

中看到了这一点

我认为您的问题属于“少数独特”类别,其中3路分区快速排序和shell sort非常快。

<强>更新

我基于sorting-algorithms.com上的伪代码实现了一些排序算法,并在OP给出的样本数据上运行它们。只是为了好玩:

插入0.154s

shell 0.031s

快速排序0.018s

基数0.017s

3-way quick sort 0.013s

答案 2 :(得分:2)

似乎可以选择Radix SortBucket sort,因为它们可以对整数有效。

对于具有k个或更少位数的n个键,基数排序的效率为O(k·n)。有时k被表示为常数,这将使基数排序比基于最佳比较的排序算法更好(对于足够大的n),所有排序算法都是O(n·log(n))。对于n个密钥和k个桶,桶排序为O(N * k)。

它可能归结为基数排序的常数(K)因子。来自我的Java experimentation。此外,值得注意的是,基数与排序元素不太一致。

100k整数:

Algorithm           Random  Sorted  Reverse Sorted
Merge sort          0.075   0.025   0.025
Quicksort           0.027   0.014   0.015
Heap sort           0.056   0.03    0.03
Counting sort       0.022   0.002   0.004
Radix sort          0.047   0.018   0.016

500k整数:

Algorithm           Random  Sorted  Reverse Sorted
Merge sort          0.286   0.099   0.084
Quicksort           0.151   0.051   0.057
Heap sort           0.277   0.134   0.098
Counting sort       0.046   0.012   0.01
Radix sort          0.152   0.088   0.079

1M整数:

Algorithm           Random  Sorted  Reverse Sorted
Merge sort          0.623   0.18    0.165
Quicksort           0.272   0.085   0.084
Heap sort           0.662   0.286   0.207
Counting sort       0.066   0.022   0.016
Radix sort          0.241   0.2     0.164

10M整数:

Algorithm           Random  Sorted  Reverse Sorted
Merge sort          7.086   2.133   1.946
Quicksort           4.148   0.88    0.895
Heap sort           11.396  3.283   2.503
Counting sort       0.638   0.181   0.129
Radix sort          2.856   2.909   3.901

当常量开始偏向基于快速排序的基数排序时,似乎500k项目。

答案 3 :(得分:2)

有一种算法可以利用已排序的子序列。它是Merge Sort的变体,名为Natural Merge Sort。我在C中找不到一个很好的实现示例,但从头开始实现起来并不太难。基本上它是这样的:

  1. 您需要一个包含两个整数的结构,即子序列的索引和长度。创建这些结构的新数组(或可能是链表)。
  2. 遍历整个数组一次,每当一个值小于前一个值时,它就是一个新子序列的开始,所以创建一个新的结构并分配子序列的位置,并指定长度上一个结构的前一个子序列。
  3. 遍历您的结构并成对地对它们执行合并操作。
  4. 重复步骤3直到所有人都合并。
  5. 合并操作与Merge Sort中的合并操作相同。你有一个指向每个子序列开头的指针。较小者应位于子序列的开头,因此如果尚未将其移动到那里并将指针移动到您移动它的子序列上。继续合并两个子序列,直到它们完全排序。

    您可以将此与Oleski的答案结合起来创建一种链接列表,其中每个节点包含一个值以及值在子序列中的一行中出现的次数。然后,当您合并时,如果遇到等效值,则将它们的基数加在一起,以便一次添加几个相同的值。您不需要为此潜在优化制作哈希值。

答案 4 :(得分:1)

构建哈希表并分配数组。对于输入数组中的每个项目,检查该项是否在哈希表中。如果是,则增加其值。如果没有,请将其插入值为1的哈希表中,并将其附加到数组中。

对数组进行排序。对于数组中的每个项目,将该项目写入输出中的次数等于其在哈希表中的计数。鳍。

编辑:您可以清除并重新使用您需要排序的每个数组的哈希表。

答案 5 :(得分:0)

我会尝试一个手工编码的qsort,其特殊技巧是在每个节点存储数字,以及它发生的次数。当你再次看到它时,你会增加计数。

始终从数组中间取出枢轴,以便排序后的子序列不会给您带来一系列不良影响。

答案 6 :(得分:0)

鉴于已排序的运行,一个合理的可能性是使用in-place merge将这些运行放在一起进行大型排序运行,直到整个数组都被排序。注意,如果函数只需要一个C接口(而不是必须用C本身编写),你可以使用C ++标准库中的std::inplace_merge,但是用extern "C"链接规范编写你的函数,所以你可以在C中使用它。

答案 7 :(得分:0)

坦率地说,GNU的qsort非常好而且很难被击败,但是我最近将我的大多数qsort调用转换为Christopher Swenson对 tim_sort 的调用,可以在https://github.com/swenson/sort/blob/master/sort.h找到-真的非常好。顺便说一句,它显式地利用已经排序的段,例如数据中的段。

我正在使用C ++,并具有“模板化”克里斯托弗(纯C宏)的代码,但它可能并不会改变它确实是您的数据的绝对赢家的事实。我相信,在带有-O3的GNU-64中,以下内容没有吸引力:

排序测试:

  • qsort:00:00:25.4

  • tim_sort:00:00:08.2

  • std :: sort:00:00:15.2

(每种分类运行一百万次)。