【问题标题】:Fast algorithm implementation to sort very small list快速算法实现对非常小的列表进行排序
【发布时间】:2011-02-14 11:30:28
【问题描述】:

这是我很久以前遇到的问题。 我想我可以问问你的想法。 假设我有非常小的数字(整数)列表,4 或 8 个元素,需要快速排序。 最好的方法/算法是什么?

我的方法是使用 max/min 函数(10 个函数对 4 个数字进行排序,没有分支,iirc)。

// s(i,j) == max(i,j), min(i,j)
i,j = s(i,j)
k,l = s(k,l)
i,k = s(i,k) // i on top
j,l = s(j,l) // l on bottom
j,k = s(j,k)

我想我的问题更多地与实现有关,而不是算法类型。

此时它变得有点依赖于硬件,所以让我们假设带有 SSE3 的 Intel 64 位处理器。

谢谢

【问题讨论】:

标签: algorithm performance sorting sorting-network


【解决方案1】:

对于像这样的小型数组,您可能应该查看sorting networks。正如您在该页面上看到的,插入排序可以表示为排序网络。但是,如果您事先知道数组的大小,则可以设计一个最佳网络。看看this site,它可以帮助您找到给定数组大小的最佳排序网络(尽管我相信最佳只能保证大小为 16)。比较器甚至可以在可以并行完成的操作中组合在一起。比较器本质上与您的 s(x,y) 函数相同,但如果您真的希望它更快,则不应使用 min 和 max,因为那样您需要进行两倍的比较次数。

如果您需要这种排序算法来处理各种大小,那么您可能应该按照其他人的建议使用插入排序。

【讨论】:

  • 所以,这里是古老的答案,但是当我第一次阅读这个答案时,我误解了“最优排序网络”的概念,所以澄清一下:最优排序网络实际上并没有使用最少数量的比较器!排序网络受到限制,因为它们总是使用相同的固定比较器排列;即,在决定要比较哪些元素时没有分支。这对于硬件实现和 GPGU 来说非常有用,但这确实意味着您需要比严格必要的更多的比较,即使在相当小的数组大小(我相信从 5 起)。
【解决方案2】:

排序网络可以很容易地在 SIMD 中实现,尽管它在 N = 16 左右开始变得丑陋。对于 N = 4 或 N = 8,尽管这将是一个不错的选择。理想情况下,您需要同时对大量小数据集进行排序,即如果您要对 8 位值进行排序,那么您至少需要对 16 个数据集进行排序 - SIMD 向量做这种事情要困难得多.

另请参阅:Fastest sort of fixed length 6 int array

【讨论】:

    【解决方案3】:

    要对少量数字进行排序,您需要一个简单的算法,因为复杂性会增加更多开销。

    对例如四个项目进行排序的最有效方法是将排序算法分解为线性比较,从而消除所有开销:

    function sort(i,j,k,l) {
      if (i < j) {
        if (j < k) {
          if (k < l) return [i,j,k,l];
          if (j < l) return [i,j,l,k];
          if (i < l) return [i,l,j,k];
          return [l,i,j,k];
        } else if (i < k) {
          if (j < l) return [i,k,j,l];
          if (k < l) return [i,k,l,j];
          if (i < l) return [i,l,k,j];
          return [l,i,k,j];
        } else {
          if (j < l) return [k,i,j,l];
          if (i < l) return [k,i,l,j];
          if (k < l) return [k,l,i,j];
          return [l,k,i,j];
        }
      } else {
        if (i < k) {
          if (k < l) return [j,i,k,l];
          if (i < l) return [j,i,l,k];
          if (j < l) return [j,l,i,k];
          return [l,j,i,k];
        } else if (j < k) {
          if (i < l) return [j,k,i,l];
          if (k < l) return [j,k,l,i];
          if (j < l) return [j,l,k,i];
          return [l,j,k,i];
        } else {
          if (i < l) return [k,j,i,l];
          if (j < l) return [k,j,l,i];
          if (k < l) return [k,l,j,i];
          return [l,k,j,i];
        }
      }
    }
    

    但是,您添加的每个额外项目的代码都会增加很多。添加第五个项目会使代码大约大四倍。八个项目大约有 30000 行,所以虽然它仍然是最有效的,但它的代码很多,你必须编写一个程序来编写代码以使其正确。

    【讨论】:

    • 原始程序使用了类似的东西,但性能非常低,我猜是由于分支问题
    • @aaa:我明白了......好吧,要消除所有分支,您可以进行所有需要的比较并将结果组合成一个键,并使用它从预先计算的字典中获取索引数组所有可能的结果。
    • 这个算法很好,但不是最优的:它最多可以进行 6 次比较,而最优算法不应该超过 5 次。
    【解决方案4】:

    我看到您已经有了一个使用 5 次比较的解决方案(假设 s(i,j) 比较两个数字一次,并且交换它们或不交换它们)。如果您坚持基于比较的排序,那么您将无法进行少于 5 次的比较。

    这可以证明,因为有 4 个! = 24 种可能的方式来订购 4 个号码。每次比较只能将可能性减半,因此通过 4 次比较,您只能区分 2^4 = 16 种可能的排序。

    【讨论】:

      【解决方案5】:

      插入排序被认为最适合小型数组。见Fast stable sort for small arrays (under 32 or 64 elements)

      【讨论】:

      • 嗨。我对实现方法更感兴趣
      • 我不确定您所说的“实施方法”是什么意思。您在寻找关于汇编代码的讨论吗?
      • 不是那么低,但会显示分支/指令
      • 我在 C++ 中为嵌入式平台实现了插入排序。结果证明在所有情况下编译后的二进制大小都是最佳的,可能是因为优化器能够在那些有益的情况下将排序分解为排序网络(当你在编译时有一个已知大小的列表时)。
      【解决方案6】:

      对于这么小的数据集,您希望算法尽可能简单。很可能,基本的Insertion Sort 可以满足您的需求。

      需要更多地了解正在运行的系统,每秒需要进行多少次这种排序,等等......但小分类的一般规则是保持简单。快速排序之类的东西没有好处。

      【讨论】:

      • 你好,我写了一些说明。我更多地寻找实施思路
      猜你喜欢
      • 2023-03-30
      • 1970-01-01
      • 2015-09-08
      • 2023-01-31
      • 2021-12-15
      • 2018-03-17
      • 2018-09-20
      • 2022-06-21
      • 1970-01-01
      相关资源
      最近更新 更多