【问题标题】:Most effective Algorithm to find maximum of double-precision values找到最大双精度值的最有效算法
【发布时间】:2014-12-07 01:14:03
【问题描述】:

在一组变量中找到最大值的最有效方法是什么?

我看到了解决方案,such as

private double findMax(double... vals) {
double max = Double.NEGATIVE_INFINITY;

for (double d : vals) {
   if (d > max) max = d;
}
    return max;
}

但是,最有效的算法是什么?

【问题讨论】:

  • 嗯似乎是个好问题。因为我认为您总是需要遍历集合中的所有值,所以我会将其简化为“找到最快的方法来做if (d>max) max=d
  • 就运行时和 Big-O 表示法而言,您必须至少触摸/加载和比较每个元素一次。您的算法正是这样做的,所以如果复杂性来自加载或比较,它似乎是最佳的。保持语言独立我会说在许多情况下并行化可能会有所帮助,即在 GPU 上,并行缩减操作是有效的。我认为其他一切都取决于语言和/或硬件(编译器)。
  • 保持集合排序并返回最后一项。
  • @rupps :使用例如OpenMP >3.1 你可以通过在for-loop 之前添加#pragma omp parallel for reduction(max : max) 来做到这一点。其中第二个最大值指的是double max。它会自动穿过管道。
  • 通过设置变量 double max = vals[0]; 可以实现边际性能提升,即设置为第一个值并从第二个值开始循环;它应该适用于任何实际实现(C、C# 等)。

标签: performance algorithm language-agnostic max double-precision


【解决方案1】:

如果列表未排序,您无法将复杂性降低到O(n) 以下...但是您可以将常数因子提高很多。使用 SIMD。例如,在 SSE 中,您将使用 MAXSS 指令在单个周期内执行 4 次比较 + 选择操作。稍微展开循环以降低循环控制逻辑的成本。然后在循环之外,从 SSE 寄存器中捕获的四个值中找出最大值。

这对任何大小的列表都有好处...对于非常大的列表也可以使用多线程。

【讨论】:

    【解决方案2】:

    假设列表中没有任何特定顺序的元素,您在问题中提到的算法是最佳的。它必须查看每个元素一次,因此它所花费的时间与列表的大小成正比,O(n)

    没有算法可以找到上限低于O(n) 的最大值。

    证明:假设有一个算法可以在小于O(n) 的时间内找到列表的最大值。那么必须至少有一个元素不检查。如果算法选择该元素作为最大值,则攻击者可以为该元素选择一个值,使其小于检查的元素之一。如果算法选择任何其他元素作为最大值,则攻击者可能会为该元素选择一个值,使其大于其他元素。无论哪种情况,算法都无法找到最大值。

    【讨论】:

      【解决方案3】:

      编辑:这是我的尝试答案,但请查看@BenVoigt 提出了优化表达式的更好方法的评论


      • 您需要至少遍历整个列表一次
      • 因此,如果有的话,需要为if (d>max) max=d 找到更有效的表达式。

      假设我们需要一般情况列表未排序(如果我们保持排序,我们只需选择最后一项作为 cmets 中的@IgnacioVazquez 点),并研究一点关于 分支预测(Why is it faster to process a sorted array than an unsorted array?,见第四个答案),看起来像

       if (d>max) max=d;
      

      可以更有效地重写为

       max=d>max?d:max; 
      

      原因是,第一条语句通常被翻译成分支虽然它完全依赖于编译器和语言,但至少在 C 和 C++ 中,甚至在基于 VM 的像 Java 这样的语言发生),而第二种语言被翻译成条件移动

      如果预测出错(必须重置执行管道),现代处理器会在分支中受到很大惩罚,而条件移动是不影响管道的原子操作。

      列表中元素的随机性(一个可以大于或小于当前最大值的概率相等)会导致许多分支预测出错。

      请参阅链接的问题,以对所有这些以及基准进行很好的讨论。

      【讨论】:

      • ...也许如果您关闭了优化。不,除非您的数据来自布朗过程,否则两个分支的概率不相等。
      • 我很好奇,为什么不呢?如果列表完全未排序,编译器怎么能猜出哪种方式更受欢迎?我链接的问题中没有完全描述这种情况吗?
      • 当你到达中点时,有 50% 的机会条件将永远不会再次为真。如果最大值在每个鸽巢中的机会均等,则在迭代i 时采用分支的概率为1 / (1 + i)。与子列表 (0,i) 的最大值在最后一个槽中的机会相同。变小,速度很快(尽管速度不够快,无法收敛)。
      • 嗯,你又是对的 ;) ...我链接的问题与中间的值进行了比较。无论如何,您认为用分支替换条件移动会带来(非常边缘的)优化吗? (编译器可以在优化阶段自动做的事情,同意!)
      • 对不起,我自相矛盾地对两件不同的事情说“最好”。第一个是“最好的”generic 三元运算符实现。第二个是最大值(或最小值)所特有的。
      猜你喜欢
      • 1970-01-01
      • 2019-01-14
      • 2021-08-30
      • 1970-01-01
      • 2018-07-09
      • 2019-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多