【问题标题】:Python calculate factorial, OverflowError: int too large to convert to floatPython 计算阶乘,OverflowError: int too large to convert to float
【发布时间】:2018-12-16 14:18:40
【问题描述】:

我想写一个函数来计算 (1 / n!) * (1! + 2! + 3! + ... + n!) 以 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

备注

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

【问题讨论】:

  • 在 IRB 中尝试 2**9999float(2**9999)
  • 你能从 codewars 发布原始和完整的问题吗?

标签: python python-3.x factorial


【解决方案1】:

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

【讨论】:

  • 谢谢。但是我在 Codewars 中做这个问题,我不认为我可以导入任何库,您还有其他解决方案吗?
【解决方案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.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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-01
    • 1970-01-01
    • 2022-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-16
    相关资源
    最近更新 更多