我试图计算(3e28选择2e28)/ 2 ^(3e28)。我试过scipy.misc.comb计算3e28选择2e28,但它给了我inf。当我计算2 ^(3e28)时,它引发了OverflowError:(34,'结果太大')。我如何计算或估算(3e28选择2e28)/ 2 ^(3e28)?
答案 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
编辑:我刚刚注意到你正在处理的数字的大小 - 对我的建议来说太大了。