二进制搜索“无限”序列。我从哪里开始?

时间:2011-09-15 16:23:51

标签: python language-agnostic binary-search

我有一个有趣的问题。我面临一个需要很长时间才能根据某个索引计算值的函数。称之为takes_a_long_time(index)。从此函数返回的值保证具有全局最小值,但不保证与之关联的索引将接近于零。

由于takes_a_long_time采用任意大的正整数作为其索引,因此对如何开始二进制搜索有唯一的约束。我需要一种方法来创建一个有限的间隔来搜索确切的最小值。我的第一个想法是从零开始检查越来越大的间隔。类似的东西:

def find_interval_with_minimum():
    start = 0
    end = 1
    interval_size = 1
    minimum_in_interval = check_minimum_in(start, end)
    while not minimum_in_interval:
        interval_size = interval_size * 2
        start = end
        end = start + interval_size
        minimum_in_interval = check_minimum_in(start, end)
    return start, end

这似乎工作正常,但还有一个额外的细节,真正抛弃了一些东西。当索引接近零时,takes_a_long_time需要指数级的时间来计算值。由于check_minimum_in需要多次调用takes_a_long_time,因此我希望避免从零开始。

所以我的问题是,鉴于最小值可能在[0,+无穷大]的任何地方,是否有任何合理的方法可以“倒退”?或者,是否有一些好的启发式方法可以避免在没有必要的情况下检查低指数?

我喜欢与语言无关的解决方案。但是,我是用Python编写的,所以如果有一个特定于python的方法,我也会这样做。

2 个答案:

答案 0 :(得分:2)

从评论到问题,曲线表现良好,您可以使用ternary search之类的东西。唯一的问题是如何处理不方便的行为,因为你的方法为零。所以不要从零开始:使用g从函数f定义一个新函数g(x) = f(1/x)。从x=0开始搜索一个小值,加倍或以其他方式增加间隔大小,直到它包含最小值。

要做到这一点,你需要知道f的极限,因为它的参数接近无穷大,或者g的等价极限,因为它的参数变为零。如果无法明确评估,我会尝试数值近似。

有关如何增加间隔大小的一些要点,请参阅答案的评论,尤其是Steve Jessop。

答案 1 :(得分:1)

听起来要做的就是选择一个很大的数字,大到takes_a_long_time不会花费太长时间才能被接受。开始两个线程:一个开始朝向包含最小值的范围向正无穷大查找,另一个向包含最小值的范围开始向下看零。由于指数时间的增加,就搜索而言,0也可能处于无限远。无论哪个线程找到结果,取消另一个。

但是,除非你想利用多个CPU内核,否则不要启动两个线程(如果你这样做,不要启动两个线程,每个核心启动一个左右)。只是替代在一边或另一边做工作。

鉴于这个基本策略,现在你需要调整你接近0的速率。你接近它的速度越快,找到最小值的步骤就越少,如果真的在那一边,但是剩下的范围就越大。搜索,因为平均而言,你将“超越”进一步向零。如果性能曲线是倒数指数,那么可能你想要尽可能少地超调,所以应该非常缓慢地接近0。甚至可能是你的任务在计算上是不可行的,“指数”通常意味着“不可能”。

显然,我不能说最初的“大数”应该是什么。百可忍受吗?百万?格雷厄姆的号码?如果您甚至不知道什么可能具有可接受的性能,您可以通过并行运行(再次,通过线程或通过dovetailing)找出不同索引的takes_a_long_time计算集,直到其中一个完成。同样,不能保证这在计算上是可行的 - 如果适合计算机内存的每个索引都需要至少10亿年,即使你在理论上有解决方案,你也会陷入困境。