我正在尝试完成以下练习: 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吗?我其实很困惑。
答案 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)
>>>
显然,避免浮点是最好的选择