(编辑说明:还添加了整数 nth root 波纹管)
你在对数的正确轨道上,但你做错了数学,你也跳过了你不应该的数字,只测试所有偶数或所有奇数,而不考虑一个数字可以是偶数权力,反之亦然
检查一下
>>> math.log(170**3,3)
14.02441559235585
>>>
甚至没有接近,正确的方法在这里描述Nth root
即:
设x为计算第N个根的数,n表示根,r为结果,那么我们得到
rn = x
从两边取任意基数,求解r
logb(rn) = logb(x)
n * logb( r ) = logb( x )
logb( r ) = logb( x ) / n
blogb( r ) = blogb( x ) / n
r = blogb( 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 根也表示为 x1/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
>>>
原因是 8191107 太简单了,它有 419 位,大于可表示的最大浮点数,但用 log 减少它会产生更合理的数字
编辑 2
现在,如果您想处理大得离谱的数字,或者只是不想完全使用浮点运算而只使用整数运算,那么最好的做法是使用method of Newton,这很有帮助link 由 Tin Peters 提供,用于立方根的特殊情况,在维基百科文章旁边向我们展示一般的方法
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 与 isPPv2
>>> x = (1 << 53) + 1
>>> x
9007199254740993
>>> isPP(x**2)
>>> isPPv2(x**2)
(9007199254740993, 2)
>>>
显然,避免浮点是最好的选择