在钟形值列表中找到最大值的快速算法

Fast algorithm for finding maximum in a bell-shaped list of values

我有一个值列表,它增加到最大值然后再次减少(这是观察到的 gaussian/bell-shaped 分布)。

values = [0, 4, 5, 15, 30, 20, 10, 5, 0];

但是分布也可以偏移:

values = [0, 0, 0, 1, 2, 3, 8, 15, 30];

或类似的:

values = [30, 20, 5, 2, 1, 1, 0, 0, 0];

在此特定应用程序中,确定特定索引处的值非常昂贵,因此使用尽可能少的数组查找很重要。

hill climbing or a variant of binary search 等解决方案应该有效。步数最少的算法是什么?

较长的查找时间是由于真实世界的测量设备(时间以秒为单位)造成的。

假设您没有局部最大值(因为测量通常会发生这种情况),二进制查找是最快的方法。例如,如果您有 1000 个数据点,那么在最大值位于中间某处的最坏情况下,您最终会进行大约 10 次检查。

为了应对最大值位于数据右侧或左侧的情况(如第二个和第三个示例),您可以简单地从检查两端是否高于其最大值开始连续点,如果确实如此,您将以不超过两次检查结束搜索。

正如 FDavidov 提到的,您应该使用二进制搜索的变体,因为在最坏的情况下您只需要访问大约 ceil[O(logn)] 个索引。

二进制搜索变体的伪代码如下所示:

left := 0
right := n - 1
while left < right do
    mid := left + (right - left) / 2
    if values[mid] > values[mid + 1] 
         right := mid
    else
         left := mid 
end

print left

然而,要在非凸形图形中找到最大或最小点,ternary search 效果最好。但是三元搜索削减 space 基于一些最不适合整数的非整数评估函数。

如果您不需要精确结果并且可以接受近似值,您也可以尝试三元搜索。

您正在寻找三元搜索,可能使用插值搜索的一些启发式搜索。

基本上,从

开始
def search(f, begin, end):
    if end - begin <= 3:
        return max(map(f, range(begin, end)))
    low = (begin * 2 + end) / 3
    high = (begin + end * 2) / 3
    if f(low) > f(high):
        return search(f, begin, high - 1)
    else:
        return search(f, low + 1, end)

渐近地,这是您在不依赖值的某些属性的情况下可以做的最好的事情。如果不是 "good enough",请更改 lowhigh 的表达式以更好地匹配您的真实数据。