在一组变量中找到最大值的最有效方法是什么?
我见过解决方案,such as
private double findMax(double... vals) {
double max = Double.NEGATIVE_INFINITY;
for (double d : vals) {
if (d > max) max = d;
}
return max;
}
但是,这样做最有效的算法是什么?
答案 0 :(得分:2)
如果列表未排序,则无法将复杂度降低到O(n)
以下......但您可以大量改进常量因子。使用SIMD。例如,在SSE中,您将使用MAXSS
指令在单个周期中执行4-ish比较+选择操作。稍微展开循环以降低循环控制逻辑的成本。然后在循环外部,找到SSE寄存器中捕获的四个值中的最大值。
这为任何大小的列表带来了好处......对于非常大的列表,使用多线程也是有意义的。
答案 1 :(得分:0)
假设列表没有任何特定顺序的元素,您在问题中提到的算法是最佳的。它必须查看每个元素一次,因此需要时间与列表的大小O(n)
成正比。
没有找到上限低于O(n)
的最大值的算法。
证明:假设存在一种算法,该算法在小于O(n)
时间内找到列表的最大值。然后必须至少有一个它不检查的元素。如果算法选择该元素作为最大值,则攻击者可以选择该元素的值,使其小于所检查的元素之一。如果算法选择任何其他元素作为最大值,则攻击者可以为元素选择一个值,使其大于其他元素。在任何一种情况下,算法都无法找到最大值。
答案 2 :(得分:0)
编辑:这是我的尝试答案,但请查看@BenVoigt建议更好地优化表达的方法
if (d>max) max=d
会找到更有效的表达方式。假设我们需要一般情况,其中列表未排序(如果我们保持排序,我们只需在评论中选择最后一项作为@IgnacioVazquez点),并研究一下关于分支预测(Why is it faster to process a sorted array than an unsorted array?,请参阅第4个答案),看起来像
if (d>max) max=d;
可以更有效地重写为
max=d>max?d:max;
原因是,第一个语句通常被翻译成分支(虽然它完全依赖于编译器和语言,但至少在C和C ++中,甚至在基于VM的语言如Java发生),而第二种语言则转换为条件移动。
如果预测出错(执行流水线必须重置),现代处理器在分支中会有很大的损失,而条件移动是一种不会影响流水线的原子操作。
列表中元素的随机性(一个可能大于或小于当前最大值且概率相等)将导致许多分支预测出错。
请参阅链接的问题,以便与基准测试一起讨论所有这些问题。