【问题标题】:Finding the turning point of a function寻找函数的转折点
【发布时间】:2018-04-30 00:26:24
【问题描述】:

我想为我的函数输出True 的第一个值。我目前的搜索效果很好,但我认为仍然有点低效。谁能建议一个更好的二分搜索?我的代码在下面,经过简化。

guess = 2
limits = [2, 2**35] #The search area

while True:
    if myFunction(guess) == False:
        limits[0] = max(limits[0], guess) #Limit the search area
        guess *= 2
    else:
        limits[1] = min(limits[1], guess) #Limit the search area
        guess = int((limits[0] + limits[1])/2) #The guess is the midpoint of the search area
        if myFunction(guess) == True and myFunction(guess-1) == False:
            return guess

【问题讨论】:

  • “第一”是什么意思? 范围内的任何 元素满足您的条件?还是最小的
  • 是的,最小的。
  • 那么没有理由在范围的中间进行猜测,对吗?如果您希望范围内的第一个值符合您的条件,那么您只需编写next((x for x in my_range if myFunction(x)), None)。如果这不是您需要的,请告诉我们。
  • @RayToal 我对这个问题的解释(我承认这可能是错误的)是myFunction 对于小于某个数字 Nx 的值返回 False,并且对于所有值返回 True的x 大于或等于N。目标是找到N。因此,可以使用二分查找来查找N,同时调用myFunction 不超过35 次。但是next 会进行线性搜索,最多可以调用myFunction 340 亿次。
  • 别担心,我没有花足够的时间阅读它。没关系。我不认为你能比二分搜索做得更好。从中间开始,然后走另一条路。即使知道转折点是更接近开始还是结束,也不会比最简单的二分搜索带来那么多好处,因为 2**35 的搜索空间只需要 35 次探测。

标签: python algorithm binary-search


【解决方案1】:

这是寻找单调递增或递减函数的水平交叉的经典问题。如您所料,binary search 可以解决。您的代码有一些错误,这并不奇怪:

虽然想法很简单,但正确实现二分搜索需要注意其退出条件和中点计算的一些细微之处。

因此,您应该尽可能避免编写自己的二进制搜索。幸运的是,Python 提供了一个库模块bisect 可以为您完成这项工作。

from bisect import bisect_left

MIN = 2
MAX = 2**35

def search(f):
   # Finds the first position of `True` in `f`
   return bisect_left(f, True, lo=MIN, hi=MAX + 1)

不要对bisect 仅适用于可索引对象这一事实感到困惑:无需使用2**35 元素创建列表。您可以使用生成器对象,而不是使用 __getitem__ 语法。为此,请将您的函数封装在一个类中并定义 getter 方法,该方法将为感兴趣点左侧的所有参数值返回 False,否则返回 True

def myFunction1(index):
   return index >= 1456
def myFunction2(index):
   return index >= 2
def myFunction3(index):
   return index >= MAX - 1

class F:
   def __init__(self, f):
      self.f = f
   def __getitem__(self, index):
      return self.f(index)

# testing code
print(search(F(myFunction1))) # prints 1456
print(search(F(myFunction2))) # prints 2
print(search(F(myFunction3))) # prints MAX - 1

【讨论】:

  • 我从未使用过 Python 类。这可以在没有课程的情况下完成吗?如果没有,我如何在类中定义我的函数?如何调用该函数 - 假设它的名称是 myFunction?抱歉,我在这方面完全是菜鸟。
  • 我更改了示例,以便单独定义 myFunction 的示例 - 希望这可以使其更清楚。为此,我怀疑您可能必须使用一个类:这只是一种欺骗解释器接受它的方法。
  • 如果您不想使用它,您可以搜索二进制搜索的 Python 实现并复制粘贴其中之一:周围有很多,即使在 Stack Overflow 上也是如此。
  • 我搞定了。谢了哥们!我遇到的唯一问题是在尝试诸如 2^35 之类的值时系统的最大 int 大小,但否则非常方便!
猜你喜欢
  • 1970-01-01
  • 2018-04-11
  • 1970-01-01
  • 2012-07-26
  • 2013-11-25
  • 1970-01-01
  • 1970-01-01
  • 2013-06-23
  • 2021-11-22
相关资源
最近更新 更多