使用python计算非常大的数字

时间:2014-12-17 01:58:59

标签: python numbers scipy calculator

我试图计算(3e28选择2e28)/ 2 ^(3e28)。我试过scipy.misc.comb计算3e28选择2e28,但它给了我inf。当我计算2 ^(3e28)时,它引发了OverflowError:(34,'结果太大')。我如何计算或估算(3e28选择2e28)/ 2 ^(3e28)?

4 个答案:

答案 0 :(得分:3)

使用斯特林的近似值(在1e10 +范围内非常准确),结合对数:

(3e28 choose 2e28) / 2^(3e28) = 3e28! / [(3e28 - 2e28)! * 2e28!] / 2^(3e28)
= e^ [log (3e28!) - log((3e28-2e28)!) - log(2e28!) - 3e28 * log(2)]

并从那里应用斯特林的近似值:

log n! ~= log(sqrt(2*pi*n)) + n*log(n) - n

你会得到答案。


以下是此近似值准确度的示例:

>>> import math
>>> math.log(math.factorial(100))
363.73937555556347
>>> math.log((2*math.pi*100)**.5) + 100*math.log(100) - 100
363.7385422250079

对于100 !,它在对数空间中的关闭小于0.01%。

答案 1 :(得分:1)

您可以使用大n的二项式的正态近似来计算此比率。如果n很大,则k必须相对接近n/2(n choose k) / 2^n不能忽略不计。

代码

以下是一些可以计算出来的代码:

def n_choose_k_over_2_pow_n(n, k):
    # compute the mean and standard deviation of the normal
    # approximation
    mu = n / 2.
    sigma = np.sqrt(n) * 1/4.

    # now transform to a standard normal variable
    z = (k - mu) / sigma

    return 1/np.sqrt(2*np.pi) * np.exp(-1/2. * z**2)

那样:

>>> n_choose_k_over_2_pow_n(3e28, 2e28)
0.0
>>> n_choose_k_over_2_pow_n(3e28, 1.5e28)
0.3989422804014327

如您所见,计算下溢。一个解决方案是计算答案的 log ,我们可以用这段代码来做:

def log_n_choose_k_over_2_pow_n(n, k):
    # compute the mean and standard deviation of the normal
    # approximation
    mu = n / 2.
    sigma = np.sqrt(n) * 1/4.

    # now transform to a standard normal variable
    z = (k - mu) / sigma

    # return the log of the answer
    return -1./2 * (np.log(2 * np.pi) + z**2)

另一个快速检查:

>>> log_n_choose_k_over_2_pow_n(3e28, 2e28)
-6.6666666666666638e+27
>>> log_n_choose_k_over_2_pow_n(3e28, 1.5e28)
-0.91893853320467267

如果我们对这些进行取幂,我们将得到之前的答案。

解释

我们可以通过呼吁统计结果来实现这一目标。二项分布由下式给出:

P(K = k) = (n choose k) p^k * p^(n-k)

对于较大的n,这通过正态分布得到很好的近似,均值为n*p和方差n*p*(1-p)

p设为1/2。然后我们有:

P(K = k) = (n choose k) (1/2)^k * (1/2)^(n-k)
         = (n choose k) (1/2)^n
         = (n choose k) / (2^n)

这恰恰是您比例的形式。因此,在转换为具有均值n/2和方差n/4的标准正态变量后,我们可以通过简单评估标准正态分布pdf来计算您的比率。

答案 2 :(得分:1)

以下使用我的回答here中的log2comb

from math import log
from scipy.special import gammaln


def log2comb(n, k):
    return (gammaln(n+1) - gammaln(n-k+1) - gammaln(k+1)) / log(2)


log2p = log2comb(3e28, 2e28) - 3e28
print "log2p =", log2p

打印

log2p = -2.45112497837e+27

所以你的号码的base-2 对数约为-2.45e27。如果您尝试计算2 ** log2p,则得到0.也就是说,该数字小于标准64位浮点数可表示的最小正数。

答案 3 :(得分:0)

有些python库允许你进行任意精度算术。例如,在SymPy中使用的mpmath。

您必须重写代码才能使用库函数。

http://docs.sympy.org/latest/modules/mpmath/basics.html?highlight=precision

编辑:我刚刚注意到你正在处理的数字的大小 - 对我的建议来说太大了。