【问题标题】:How to find integer nth roots?如何找到整数 n 次根?
【发布时间】:2013-04-12 18:52:40
【问题描述】:

我想找到小于或等于 n 的第 k 个根的最大整数。我试过了

int(n**(1/k))

但是对于 n=125, k=3 这给出了错误的答案!我碰巧知道 5 的立方是 125。

>>> int(125**(1/3))
4

什么是更好的算法?

背景:在 2011 年,这个失误让我击败了 Google Code Jam 问题Expensive Dinner

【问题讨论】:

  • 舍入导致您观察到的结果。在这里,这是因为 1/3 向下舍入。我不知道 Python 的 IEEE754 合规性,但在其他机器上你可以观察到 5。
  • @Alexandre Huh, 125**(1/3) -> 4.999999999999999

标签: python algorithm math nth-root


【解决方案1】:

一种解决方案首先通过重复将 hi 乘以 2 直到 n 介于 lo 和 hi 之间,将 lo 和 hi 之间的答案括起来,然后使用二进制搜索来计算确切的答案:

def iroot(k, n):
    hi = 1
    while pow(hi, k) < n:
        hi *= 2
    lo = hi // 2
    while hi - lo > 1:
        mid = (lo + hi) // 2
        midToK = pow(mid, k)
        if midToK < n:
            lo = mid
        elif n < midToK:
            hi = mid
        else:
            return mid
    if pow(hi, k) == n:
        return hi
    else:
        return lo

另一种解决方案使用牛顿法,该方法对整数非常有效:

def iroot(k, n):
    u, s = n, n+1
    while u < s:
        s = u
        t = (k-1) * s + n // pow(s, k-1)
        u = t // k
    return s

【讨论】:

  • 谢谢。用彼得的例子测试它们,都给出正确的答案。
  • 这能保证找到精确的整数根吗?
  • @Ben:是的。两种方法都可以。
  • 这是为什么呢?是不是有可能卡在不同的整数上?
  • @Ben:你有失败的例子吗?
【解决方案2】:

怎么样:

def nth_root(val, n):
    ret = int(val**(1./n))
    return ret + 1 if (ret + 1) ** n == val else ret

print nth_root(124, 3)
print nth_root(125, 3)
print nth_root(126, 3)
print nth_root(1, 100)

这里,valn 都应该是整数和正数。这使得return 表达式完全依赖整数运算,消除了任何舍入错误的可能性。

请注意,只有当val**(1./n) 相当小时,才能保证准确性。一旦该表达式的结果与真实答案的偏差超过1,该方法将不再给出正确答案(它将给出与原始版本相同的近似答案)。

我仍然想知道为什么int(125**(1/3))4

In [1]: '%.20f' % 125**(1./3)
Out[1]: '4.99999999999999911182'

int() 将其截断为 4

【讨论】:

  • 也许 == 应该替换为 &lt;=,因为测试浮点相等性通常很危险。
  • @unutbu:在那个表达式中,一切都是整数(“这里,val 和 n 都应该是整数和正数”)。
  • @Andrey:尝试在解释器中输入125**(1./3)。你应该得到类似4.999999999999999,而不是5int 将其发送至 4
  • @EricPostpischil:因为这会在4 ** (1./3) 和许多其他输入上失败,而我的答案中的方法不会。
  • 小心大数字:nth_root((1020+2)**2,2)=1020 使用此答案中的方法。
【解决方案3】:

被严重烧伤后我的谨慎解决方案:

def nth_root(N,k):
    """Return greatest integer x such that x**k <= N"""
    x = int(N**(1/k))      
    while (x+1)**k <= N:
        x += 1
    while x**k > N:
        x -= 1
    return x

【讨论】:

  • 有时我发现将此方法与可变步长结合使用以加速收敛很有用(仅当您使用非常大的数字时才需要!)
  • @Peter,所以第一个猜测真的可以错多于 1?
  • 如果N 太大而无法转换为浮点数,您当然会被N**(1./k) 烧毁。对于足够小的NN**(1./k) 很接近,所以调整不会花很长时间。如果您有兴趣处理大数,其中转换为浮点数会损失太多精度,因此调整可能需要很长时间,几个 Newton-Raphson 步骤将获得精确结果。
  • int(((1040)**2)**(1.0/2))=1040+303786028427003666890752,所以它可以走很长一段路!
【解决方案4】:

为什么不试试这个:

125 ** (1 / float(3)) 

pow(125, 1 / float(3))

它返回 5.0,因此您可以使用 int() 转换为 int。

【讨论】:

    【解决方案5】:

    在 Lua 中使用 Newton-Raphson 方法

    > function nthroot (x, n) local r = 1; for i = 1, 16 do r = (((n - 1) * r) + x / (r ^ (n -   1))) / n end return r end
    > return nthroot(125,3)
    5
    > 
    

    Python 版本

    >>> def nthroot (x, n):
    ...     r = 1
    ...     for i in range(16):
    ...             r = (((n - 1) * r) + x / (r ** (n - 1))) / n
    ...     return r
    ... 
    >>> nthroot(125,3)
    5
    >>> 
    

    【讨论】:

    • nthroot(10**4,4)=32 用这个方法,我会期望 10?
    • 大数需要增加迭代次数; 10**4 range(32) 就足够了。
    • 这种方法对我来说似乎异常慢。循环计算 nthroot(10**4,4) 需要 21 次,计算 nthroot(10**15,15) 循环需要 423 次。
    【解决方案6】:

    我想知道从基于对数的方法开始是否可以帮助确定舍入误差的来源。例如:

    import math
    def power_floor(n, k):
        return int(math.exp(1.0 / k * math.log(n)))
    
    def nth_root(val, n):
        ret = int(val**(1./n))
        return ret + 1 if (ret + 1) ** n == val else ret
    
    cases = [
        (124, 3),
        (125, 3),
        (126, 3),
        (1, 100),
        ]
    
    
    for n, k in cases:
        print "{0:d} vs {1:d}".format(nth_root(n, k), power_floor(n, k))
    

    打印出来

    4 vs 4
    5 vs 5
    5 vs 5
    1 vs 1
    

    【讨论】:

      【解决方案7】:
      def nth_root(n, k):
          x = n**(1./k)
          y = int(x)
          return y + 1 if y != x else y
      

      【讨论】:

        【解决方案8】:

        您可以四舍五入到最接近的整数,而不是将 / 向下舍入为零(我不知道 Python 指定了什么):

        def rtn (x):
            return int (x + 0.5)
        
        >>> rtn (125 ** (1/3))
        5
        

        【讨论】:

        • rtn(4 ** (1./3)) 是 2,但 2**3 &gt; 4
        • @mbomb007:如果 x 为非负数,它似乎可以工作(这需要根据规范进行检查——如果 Python 中有四舍五入,则需要调整负数)。但非整数幂仅适用于非负数!
        【解决方案9】:

        int(125**(1/3)) 显然应该是 5,即正确答案,所以这必须是标准的计算机舍入误差,即在内部结果是 4.9999999999 向下舍入为 4。无论您使用什么算法,都会存在这个问题。一个简单的临时解决方案是添加一个很小的数字,例如int((125**(1/3)) + 0.00000001)

        【讨论】:

        • 问题出在 1/3 上,而不是 5:5 可以用浮点数精确表示。
        • 是的,5 可以完美地表示为浮点数,但你仍然会遇到这个问题,例如(100**(1/2)) 即使所有 100、(1/2) 和答案 10 都可以完美表示。问题是,计算机要获取权力,使用日志,而这些数字的日志并不能完美表示。
        • 添加0.00000001 只是一个技巧。例如,它会使算法在取 95367431259155 的 20 次根时失败:结果将是 5 而不是 4
        • @NPE,是的。我的语言中的“ad-hoc”=“hack”哈哈。事实是,不可能一直克服舍入误差。而不是 0.00000001,一个更接近您正在使用的机器精度的数字可能是最佳的。
        • @Stochasticly:对于整数输入,可以避免舍入错误。请参阅我的答案以了解其中一种方法。
        【解决方案10】:

        做这一切之前:

        from __future__ import division
        

        然后运行任何上述指定的技术来获得结果。

        【讨论】:

          【解决方案11】:
          def nthrootofm(a,n):
              a= pow(a,(1/n))
              return 'rounded:{},'.format(round(a))
          a=125
          n=3
          q=nthrootofm(a,n)
          print(q)
          

          刚刚使用了格式字符串,也许这有帮助。

          【讨论】:

            猜你喜欢
            • 2015-08-05
            • 2015-12-04
            • 1970-01-01
            • 2023-04-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多