我想编写一个函数,使用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.0000989217538616将被截断为1.000098 1.2125000000000001将被截断为1.2125
备注
请记住,阶乘的增长非常迅速,您需要处理大量输入。
答案 0 :(得分:3)
going(170)
可以正常工作,对吧?
您看到的是计算机表示浮点数的基本限制,而不是Python 本身的问题。通常,大多数现代计算机使用IEEE 754来表示和执行带有非整数的数学运算。具体而言,使用IEEE 754的"binary64" (double-precision)浮点表示形式的数字的最大值为2 ^ 1023×(1 +(1 − 2 ^ -52)),或约为1.7976931348623157×10 ^ 308。原来是170! ≈7.2×10 ^ 306,刚好低于最大值。但是171! ≈1.2×10 ^ 309,所以您很不走运。
实际使用较大的数字执行计算而不会遇到这些溢出错误或丢失精度的最佳机会是使用gmpy2之类的大型数字库(请参见this previous answer)。可能的解决方案是:
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
答案 1 :(得分:2)
@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.lgamma
将k!/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