【问题标题】:Is there anyway to optimize sort on this kind of data?反正有没有优化这种数据的排序?
【发布时间】:2012-06-19 02:20:56
【问题描述】:

我正在对整数键数组进行排序。

数据信息:

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

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

  1. stdlib.h qsort;

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

  2. Timsort;

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

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

    他们对快速排序和插入排序的组合是迄今为止我的数据最快的;我尝试了各种设置并将枢轴作为中间元素(不是 3 的中值)并从 28 个元素子数组(默认不是 8 个)开始插入排序提供了最佳性能

  4. shell排序;

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


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

函数在 C 中。我使用 Visual Studio 2010。 有什么想法吗?

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

【问题讨论】:

  • 你能发个样本吗?对于这样的问题,如果我们可以直接进行基准测试会有所帮助。
  • 示例发布; >>基数排序为此,我需要 135 000 000 个元素数组并对其进行迭代:) 如果我可以设计一些智能快速哈希,那么也许...顺便说一句,如果您想进行更多基准测试,我可以发布示例数据集(1k 排序) :)
  • @PiotrLopusiewicz:你会使用 C++ 吗? C++ STL 中的 map 可以快速解决这个问题。或者您可以编写平衡树作为集合使用。
  • @nhahtdh STL Map 与大多数特定问题的算法相比效率不高,主要是因为它频繁的分配和移动。 (计时看看。)
  • @Crashworks:您可能是对的,因为输入大小很小(1000 范围)。在具有大量重复键的较大输入上,它应该比排序算法更快。

标签: c algorithm sorting


【解决方案1】:

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

有了这个,你只需要让一个通过数组,另一个通过小得多的哈希表键列表。这应该避免大多数交换和比较。

【讨论】:

  • 我考虑过.. 我认为我需要快速单调散列来将范围压缩为 1.100 或其他东西并合理分布,有什么建议吗?
  • @PiotrLopusiewicz 你可以为你的散列函数取一些素数的数字。例如尝试 97。
  • @PiotrLopusiewicz 模块一些素数应该足够好。如果没有,您可以轻松配置它。
  • 不需要(或不可能)单调的哈希。只需构建哈希表(封闭的哈希看起来合适),然后获取键并对其进行正常排序(只需 33-80 个键,没问题),最后将每个键写入正确的次数。
  • 我会尝试实现它;到目前为止,我只尝试计算哈希(% 91),它已经花费了整个排序的一半时间,甚至没有将元素放置到位并分配内存;仍然,如果与大数组相比,较短的数组不需要时间,那将是显着的收益;我会告诉你的:)
【解决方案2】:

你可以看看这个animation,我从这个post看到的

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

更新:

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

插入0.154s

壳0.031s

快速排序 0.018s

基数 0.017s

3路快速排序0.013s

【讨论】:

  • 您是否有机会实现 3 路快速排序?到目前为止对我来说最快的算法是快速排序 + 插入排序(我在 OP 中链接到它),但由于大量重复,三向快速排序可能会更好。
  • @PiotrLopusiewicz 抱歉,我 1 小时前刚刚清理了所有这些文件。但是我实现的内容与伪代码 here 完全一致。这对你来说应该很容易。祝你好运!
  • 我能够通过实现 3 路快速排序来击败 stdlib qsort,但我距离击败 ucw 家伙的实现还差得很远。我想我已经尝试过了,谢谢大家的建议:)
  • @PiotrLopusiewicz 不错的尝试!很高兴帮助:)
【解决方案3】:

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

  1. 您需要一个包含两个整数的结构,即子序列的索引和长度。为这些结构创建一个新数组(或者可能是一个链表)。
  2. 遍历整个数组一次,每次一个值小于前一个值时,它就是新子序列的开始,因此创建一个新结构并分配子序列的位置,并分配长度前一个结构的前一个子序列。
  3. 遍历您的结构并对它们成对执行合并操作。
  4. 重复第 3 步,直到全部合并。

合并操作与Merge Sort中的合并操作相同。您有一个指向每个子序列开头的指针。哪个较小的应该在子序列的开头,所以如果它还没有,则将它移到那里,然后将指针移到您从中移动它的子序列上。继续合并两个子序列,直到它们完全排序。

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

【讨论】:

  • 我可能会在接下来几天的某个时间在C 写这篇文章,以便您对其进行基准测试。
  • 直觉上听起来不错,我会尝试的。如果您要实施它,您可以尝试对我的数据进行基准测试:)
  • 我实现了,还不错。我现在的实现非常幼稚,我认为这会比我现在的快一点。
  • 嘿@PiotrLopusiewicz 对不起,我还没有解决这个问题。如果你已经这样做了,我想实现我自己的版本没有意义。让我知道它是如何工作的,如果你能够对其进行任何优化:)
【解决方案4】:

似乎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 个项目。

【讨论】:

  • W/e 我尝试使用 radix 总是太慢。我想为桶分配内存会减慢速度
  • 你自己实现的还是网上找的?可以进行许多优化。如果您自己制作,请尝试定义明确的版本。 opensource.apple.com/source/Libc/Libc-167/stdlib.subproj/…
  • 此外,基数排序可以很好地并行完成;如果这是你的选择。
  • @PiotrLopusiewicz 我已经更新了我的答案以反映我的实验。
  • 感谢您的链接,我将尝试该实现
【解决方案5】:

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

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

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

【讨论】:

    【解决方案6】:

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

    始终从数组的中间取枢轴,这样排序后的子序列就不会给你一系列坏的枢轴。

    【讨论】:

      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

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

        我正在使用 C++,并且已经“模板化”了 Christopher 的(纯 C 宏)代码,但这可能不会改变这样一个事实,即它在您的数据上绝对是赢家。我相信以下在带有 -O3 的 GNU-64 中没有吸引力:

        排序测试:

        • qsort: 00:00:25.4

        • tim_sort: 00:00:08.2

        • std::sort: 00:00:15.2

        (每一种都运行一百万次)。

        【讨论】:

          猜你喜欢
          • 2020-06-15
          • 2013-04-04
          • 1970-01-01
          • 1970-01-01
          • 2019-11-04
          • 2013-09-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多