【问题标题】:Binary search for square root 2二分查找平方根 2
【发布时间】:2018-09-05 02:19:26
【问题描述】:

我有一个问题,我的二进制搜索算法找到 2 的平方根似乎处于无限循环并永远运行:

num = 2
low = 1
high = num
i = 0
while((i**2) != 2): #was while(low<high): but wasnt working with it either
 i = low + (high - low) / 2;

 sqrt = i * i

 if (sqrt == num):
     print(i)
 elif(sqrt < num):
     low = i
 else:
     high = i
print(sqrt)    

testRoot = 2 ** (.5)
print(testRoot)

我不确定我的 while 循环是否存在问题。我认为这将是一个非常简单的二进制搜索算法,稍作修改以适应平方根方面。

运行我的代码时,我似乎无法让它产生任何输出。我不确定代码或编译器是否存在真正的问题,因为我认为我遵循的算法与我过去的算法非常接近。

【问题讨论】:

  • Python 2 还是 Python 3?
  • @MadPhysicist 对不起,我要问的问题是,我的代码是错误的,还是 Repl.it 上的编译器实际上有问题?
  • 您是否知道没有任何具有有限二进制表示的数字可以平方以获得正好 2?
  • 当我运行它时,它实际上只是运行而没有产生任何输出。

标签: python binary-search numerical-methods square-root


【解决方案1】:

问题在于using == on floating point numbers 几乎总是一种不好的做法,并且有various 对此提出疑问。您应该将比较替换为abs(a - b) &lt; precision。还看了一下这个问题下的cmets,很有用。

我修改后的代码如下所示(Python 3),如果您想要更精确,请将 1e-6 替换为较小的数字。但请注意,“太精确”是不明智的,如果您希望循环停止,建议使用 1.0e-15 或更大的精度,因为浮点数本身有精度限制。

num = 2
low = 1
high = num
i = 0
while abs((i**2) - num) > 1e-6:  # Note
    i = low + (high - low) / 2
    sqrt = i * i

    if abs(sqrt - num) < 1e-6:  # Note
        print(i)
    elif(sqrt < num):
        low = i
    else:
        high = i
print(sqrt)

testRoot = 2 ** (.5)
print(testRoot)

【讨论】:

  • 如果你想让循环停止,可能要提到精度必须大于 ~5e-16。
  • 我删除了反对票。我通常认为错误的信息值得投反对票,因为其目的是帮助他人。不过,我确实尝试留下有意义的 cmets。感谢您进行修复。
  • @MadPhysicist 哎呀,虽然不是每个人都像我一样友好,但我很感激。
  • 我注意到了。当我看到错误时,我不会友好,所以如果我粗鲁,我很抱歉。那不是我的意图。
  • 话虽如此,我已经发布了一个替代解决方案。
【解决方案2】:

正如my original comment 和所有答案中所述,square root of 2irrational。每个不是完美平方的整数的平方根在这方面都是不合理的,所以 2 在这方面并不特别。重要的是,x**2 == 2 永远不会对任何有限精度的 x 成立(因为有限精度是表示数字是有理数的另一种方式)。

其他答案建议进行搜索,直到您达到某个固定的、预先确定的准确度。这很有效,特别是如果您提前知道答案的二进制数量级,那么您可以将结果的准确性设置为最后一位。

我想提出一种更自然的方法。您可以检查您的中心值是否完全等于边界之一。这意味着在您当前的猜测中,边界之间的差异的一半代表不到一位精度。您对中心的表述已经正确:i = low + (high - low) / 2 可以与 lowhigh 使用 == 进行比较,而 i = (low + high) / 2 可能不正确。这是因为high - low 的精度大于或等于任一边界的精度,而low + high 可能会丢失一些数字。

所以这是我的建议:

数 = 2 低 = 1 高 = 数量 猜测 = 低 + (高 - 低) / 2 计数 = 0 而guess != low和guess != high: sqr = 猜测 * 猜测 如果 sqr == 数量: 休息 elif(sqr

我已添加count 进行验证。 52次迭代得到结果,精度在1位以内:

52 : 2.0000000000000004
1.4142135623730951 ~= 1.4142135623730951 

对边界的最终检查(whileelse 子句)确保您获得最接近所需结果的结果,无论您先点击哪一个。

收敛是合理的:64-bit floating point number in IEEE-754 format 在尾数中有 53 位,因此您必须将搜索空间减半以获得结果(第一次在循环之外)是有道理的。

这是我用于测试的 sn-p:https://ideone.com/FU9r82

【讨论】:

  • 我不确定你的逻辑是否正确。即使存在整数 sqrt 也不一定返回整数,如果将 num 设置为 9,则结果似乎是 1。
  • @DSM。循环后的测试仅适用于没有中断的情况。已编辑
  • @DSM。除了初始分配之外,我刚刚删除了对 2 的所有其他硬编码引用
  • 检查是否达到机器精度的另一种方法是迭代 53 次(在 IEEE 双精度上下文中)。
  • @Yves。我的方法也适用于所有其他常见的浮点类型。例如,numpy 有 float128、float64、float32 等。但是,如果 num 是任意精度类型(如 Python int,但用于浮点),您的建议会很有效。
【解决方案3】:

两个浮点数相等是一个非常严格的条件,因为 2 的平方根在小数点后有无限位数。试试这个while 条件:

while (abs((i ** 2) - 2) > 1e-8)

【讨论】:

    【解决方案4】:

    进行一些探索通常会有所帮助。

    $ python
    Python 3.6.6 
    >>> import math
    
    >>> import numpy
    >>> import scipy
    >>> import numpy
    >>> math.sqrt(2) ** 2
     2.0000000000000004
    >>> numpy.sqrt(2) ** 2
     2.0000000000000004
    >>> scipy.sqrt(2) ** 2
     2.0000000000000004
    >>> (2.0**(0.5))**2
     2.0000000000000004
    >>> x =  math.sqrt(2) ** 2
    >>> math.sqrt(x)
     1.4142135623730951
    >>> math.sqrt(2)
     1.4142135623730951
    >>> x*x
     4.000000000000002
    >>> x**2
     4.000000000000002
    >>> 1.414213562373095**2
     1.9999999999999996
    >>> 1.41421356237309505**2
     2.0000000000000004
    >>> 1.41421356237309505
     1.4142135623730951
    >>> 1.41421356237309504
     1.4142135623730951
    >>> 1.41421356237309504**2
     2.0000000000000004
    >>> 1.41421356237309503**2
     1.9999999999999996
    >>> 1.41421356237309503 * 1.41421356237309504
     2.0
    >>> 1.41421356237309504 - 1.41421356237309503
     2.220446049250313e-16
    

    一点点肘部油脂可以具有教育意义。 (而且令人困惑!)

    我们来看看错误

    >>> s =set()
    >>> exact = 0
    >>> exact_over = 0
    >>> exact_under = 0
    
    >>>for i in range(100):
    ...     differ = (i - (i**0.5)**2)
    ...     s |= {differ}
    ...     exact += 1 if differ == 0 else 0
    ...     exact_over  += 1 if differ > 0  else 0
    ...     exact_under += 1 if differ < 0  else 0
    
    >>> sorted(list(s)) 
    [-1.4210854715202004e-14,
     -7.105427357601002e-15,
     -3.552713678800501e-15,
     -1.7763568394002505e-15,
     -8.881784197001252e-16,
     -4.440892098500626e-16,
     0.0,
     4.440892098500626e-16,
     8.881784197001252e-16,
     1.7763568394002505e-15,
     3.552713678800501e-15,
     7.105427357601002e-15,
     1.4210854715202004e-14]
    
    >>> exact_under, exact, exact_over
    (26, 49, 25)
    

    【讨论】:

      【解决方案5】:

      正如在其他几篇文章中所说,比较i * i == 2 不起作用。

      设计停止标准的一个简单解决方案是告诉您想要多少位的准确度。实际上,在二分搜索中,精确位的数量在每次迭代时都会增加 1。

      因此,要获得完整的双精度精度,迭代 53 次(再多也没用)。另请注意,在循环内测试相等性会适得其反。

      num= 2
      low= 1
      hig= 2
      for i in range(53):
          mid= 0.5 * (low + hig)
          if mid * mid < num:
              low= mid
          else:
              hig= mid
      
      print(mid)
      

      53 次迭代在这里是合适的,因为初始估计对于第一位 (1≤√2

      【讨论】:

        猜你喜欢
        • 2020-09-08
        • 1970-01-01
        • 1970-01-01
        • 2011-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多