Python计算factorial,OverflowError:int太大,无法转换为float



我想写一个函数,用n作为函数的参数来计算(1/n!(*(1!+2!+3!+…+n!(,并且结果被截断为6位小数(不取整(。以下是我的代码:

def going(n):
n1 = n 
n2 = n
factorial = 1
back = 1
for i in range(2, n1+1):
factorial *= i
while n2>1:
this = 1
for i in range(2, n2+1):
this*=i
back+=this
n2 = n2-1
this = 1
result = int((1/factorial)*back*1000000)/1000000
return result

当我将参数171传递到函数中时,我得到了以下回溯:

Traceback (most recent call last):
File "/Users/Desktop/going.py", line 18, in <module>
print(going(171))
File "/Users/Desktop/going.py", line 15, in going
result = int((1/factorial)*back*1000000)/1000000
OverflowError: int too large to convert to float

如何解决此问题?非常感谢你的帮助!

--更新--很抱歉我没有澄清:我在Codewars中处理这个问题,我认为我不能导入任何库来使用。因此,我需要一个可以避免使用任何库的解决方案。

Codewars的原始问题:

考虑以下数字(其中n!是阶乘(n((:

u1 = (1 / 1!) * (1!)
u2 = (1 / 2!) * (1! + 2!)
u3 = (1 / 3!) * (1! + 2! + 3!)
un = (1 / n!) * (1! + 2! + 3! + ... + n!)

哪个将获胜:1/n!或(1!+2!+3!+…+n!(?

这些数字会因为1/n而变为0吗!还是由于阶乘的和而无穷大?

任务

计算给定n的(1/n!(*(1!+2!+3!+…+n!(,其中n是大于或等于1的整数。

为了避免讨论四舍五入,返回截断到小数点后6位的结果,例如:

1.000989217538616将被截断为1.000981.21250000000001将被截断为1.2125

备注

请记住,阶乘增长相当快,并且需要处理大量输入。

going(170)按预期工作,对吗?

您所看到的是计算机表示浮点数的基本限制,而不是Python本身的问题。一般来说,大多数现代计算机使用IEEE 754来表示和执行非整数的数学运算。具体而言,使用IEEE 754的"二进制64"(双精度(浮点表示的数字的最大值为2^1023×(1+(1−2^−52((,约为1.7976931348623157×10^308。原来是170!≈7.2×10^306,刚好低于最大值。然而,171!≈1.2×10^309,所以你运气不好。

要想在不出现溢出错误或失去精度的情况下使用如此大的数字进行计算,最好的方法是使用像gmpy2这样的大数字库(请参阅前面的答案(。一个可能的解决方案是:

from gmpy2 import mpz, add, div, fac
def going(n):
factorial = fac(n)
back = mpz(1)
for i in range(2, n+1):
back = add(back, fac(i))
result = div(back, factorial)
return result

@PaSTE关于使用gmpy2的建议很好,应该很好。

库mpmath构建在gmpy2之上,并提供函数ff(下降因子(,使实现更加简洁:

import mpmath
def going_mp(n):
return sum([1/mpmath.ff(n, k) for k in range(n)])

例如,

In [54]: import mpmath
In [55]: mpmath.mp.dps = 30
In [56]: going_mp(170)
Out[56]: mpf('1.00591736819491744725806951204519')
In [57]: going_mp(171)
Out[57]: mpf('1.00588255770874220729390683925161')

(我省略了数字的截断。这是你可以根据自己的意愿添加的内容。(


处理超大数字的另一种标准技术是使用数字的对数,而不是数字本身。在这种情况下,可以使用math.lgammak!/n!计算为exp(lgamma(k+1) - lgamma(n+1))。这将允许您仅使用标准math库来计算值。

import math
def going_l(n):
lognfac = math.lgamma(n + 1)
return sum([math.exp(math.lgamma(k+1) - lognfac) for k in range(1, n+1)])

例如,

In [69]: going_l(170)
Out[69]: 1.0059173681949172
In [70]: going_l(171)
Out[70]: 1.0058825577087422

最后,如果你甚至不想使用标准库,你可以用另一种方式避免使用大数字。将表达式重写为

1      1           1                 1
1 + - + ------- + ------------- + ... + ---
n   n*(n-1)   n*(n-1)*(n-2)          n!

这导致了这种实现:

def going_nolibs(n):
total = 0.0
term = 1.0
for k in range(n, 0, -1):
total += term
term /= k
return total

例如,

In [112]: going_nolibs(170)
Out[112]: 1.0059173681949174
In [113]: going_nolibs(171)
Out[113]: 1.0058825577087422

最新更新