Python中对数和幂函数的不精确结果

时间:2016-09-18 17:59:40

标签: python

我正在尝试完成以下练习: https://www.codewars.com/kata/whats-a-perfect-power-anyway/train/python

我尝试了多种变体,但是当涉及大数字时我的代码出现故障(我尝试了多种变体,包括涉及日志和电源功能的解决方案):

练习:
您的任务是检查给定的整数是否是完美的幂。如果它是一个完美的幂,则返回一对m和k,其中m ^ k = n作为证明。否则返回Nothing,Nil,null,None或您的语言等效。

注意:对于完美的电源,可能有几对。例如,81 = 3 ^ 4 = 9 ^ 2,因此(3,4)和(9,2)是有效的解决方案。但是,测试会考虑到这一点,所以如果一个数字是一个完美的力量,那么返回任何证明它的对。

练习使用Python 3.4.3

我的代码:

import math
def isPP(n):
    for i in range(2 +n%2,n,2):
            a = math.log(n,i)
            if int(a) == round(a, 1):
                if pow(i, int(a)) == n:
                    return [i, int(a)]
    return None

问题: 怎么可能我不断得到更大数字的错误答案?我在Python 3中读到,所有的int都被认为是Python 2中的“long”,即它们可以非常大并且仍然可以准确表示。因此,由于i和int(a)都是int,所以不应该正确评估pow(i,int(a))== n吗?我其实很困惑。

1 个答案:

答案 0 :(得分:3)

(编辑注释:还添加了整数nth root bellow)

你处于正确的对数轨道,但你的数学错误,你也不会跳过你不应该的数字,只测试所有偶数或所有奇数而不考虑数字甚至是奇数权力,反之亦然

检查这个

>>> math.log(170**3,3)
14.02441559235585
>>> 

甚至没有关闭,这里描述了正确的方法Nth root

是:

让x为计算第N个根的数字,n表示root和r结果,然后我们得到

r n = x

从两边取任何基地的日志,并解决r

log b (r n )= log b (x)

n * log b (r)= log b (x)

log b (r)= log b (x)/ n

b log b (r) = b log b (x)/ n

r = b log b (x)/ n

所以例如登录基数10我们得到

>>> pow(10, math.log10(170**3)/3 )
169.9999999999999
>>> 

更接近,只需四舍五入就可以得到答案

>>> round(169.9999999999999)
170
>>> 

因此该功能应该是这样的

import math

def isPP(x):
    for n in range(2, 1+round(math.log2(x)) ):
        root   = pow( 10, math.log10(x)/n )
        result = round(root)
        if result**n == x:
            return result,n

范围的上限是为了避免测试肯定会失败的数字

测试

>>> isPP(170**3)
(170, 3)
>>> isPP(6434856)
(186, 3)
>>> isPP(9**2)
(9, 2)
>>> isPP(23**8)
(279841, 2)
>>> isPP(279841)
(529, 2)
>>> isPP(529)
(23, 2)
>>> 

修改

Tin Peters 指出您可以使用pow(x,1./n),因为数字的第n个根也表示为x 1 / n

例如

>>> pow(170**3, 1./3)
169.99999999999994
>>> round(_)
170
>>> 

但请记住,对于非常大的数字,例如

,这将失败
>>> pow(8191**107,1./107)
Traceback (most recent call last):
  File "<pyshell#90>", line 1, in <module>
    pow(8191**107,1./107)
OverflowError: int too large to convert to float
>>> 

虽然对数方法会成功

>>> pow(10, math.log10(8191**107)/107)
8190.999999999999
>>> 

原因是8191 107 简单太大,它有419位数,大于最大浮点数可表示,但用日志减少它会产生更合理的数字

编辑2

现在如果你想处理数字大得可笑,或者只是简单地不想完全使用浮点运算而只使用整数运算,那么最好的做法是使用method of Newton,针对立方体根的特定情况,由 Tin Peters 提供的有用的link,向我们展示了与维基百科文章一起执行此操作的方法

def inthroot(A,n):
    if A<0:
        if n%2 == 0:
            raise ValueError
        return - inthroot(-A,n)
    if A==0:
        return 0
    n1 = n-1
    if A.bit_length() < 1024: # float(n) safe from overflow
        xk = int( round( pow(A,1/n) ) )
        xk = ( n1*xk + A//pow(xk,n1) )//n  # Ensure xk >= floor(nthroot(A)).
    else:
        xk = 1 << -(-A.bit_length()//n)  # power of 2 closer but greater than the nth root of A
    while True:
        sig = A // pow(xk,n1)
        if xk <= sig:
            return xk
        xk = ( n1*xk + sig )//n

通过 Mark Dickinson 检查解释,以了解算法在多维数据集根情况下的工作情况,这与此基本相同

现在让我们将其与另一个

进行比较
>>> def nthroot(x,n):
        return pow(10, math.log10(x)/n )

>>> n = 2**(2**12) + 1  # a ridiculously big number
>>> r = nthroot(n**2,2)
Traceback (most recent call last):
  File "<pyshell#48>", line 1, in <module>
    nthroot(n**2,2)
  File "<pyshell#47>", line 2, in nthroot
    return pow(10, math.log10(x)/n )
OverflowError: (34, 'Result too large')
>>> r = inthroot(n**2,2)
>>> r == n
True
>>> 

然后该功能现在

import math

def isPPv2(x):
    for n in range(2,1+round(math.log2(x))):
        root = inthroot(x,n)
        if root**n == x:
            return root,n

测试

>>> n = 2**(2**12) + 1  # a ridiculously big number
>>> r,p = isPPv2(n**23)
>>> p
23
>>> r == n
True
>>> isPPv2(170**3)
(170, 3)
>>> isPPv2(8191**107)
(8191, 107)
>>> isPPv2(6434856)
(186, 3)
>>>

现在让我们检查isPP vs isPPv2

>>> x = (1 << 53) + 1
>>> x
9007199254740993
>>> isPP(x**2)
>>> isPPv2(x**2)
(9007199254740993, 2)
>>>     
显然,避免浮点是最好的选择