我想计算
尽可能准确地获取n
最多1000000
的值。这是一些示例代码。
from __future__ import division
from scipy.misc import comb
def M(n):
return sum(comb(n,k,exact=True)*(1/n)*(1-k/n)**(2*n-k)*(k/n)**(k-1) for k in xrange(1,n+1))
for i in xrange(1,1000000,100):
print i,M(i)
第一个问题是OverflowError: long int too large to convert
时我n = 1101
会浮动。这是因为comb(n,k,exact=True)
太大而无法转换为浮点数。然而,最终结果始终是0.159
附近的数字。
我在How to compute sum with large intermediate values问了一个相关的问题但是这个问题因三个主要原因而有所不同。
不崩溃的解决方案是使用
from fractions import Fraction
def M2(n):
return sum(comb(n,k,exact=True)*Fraction(1,n)*(1-Fraction(k,n))**(2*n-k)*Fraction(k,n)**(k-1) for k in xrange(1,n+1))
for i in xrange(1,1000000,100):
print i, M2(i)*1.0
不幸的是,现在这么慢,我在合理的时间内没有得到n=1101
的答案。
所以第二个问题是如何让它足够快以完成大n
。
答案 0 :(得分:4)
您可以使用对数变换计算每个加数,该变换分别用加法,减法和乘法替换乘法,除法和乘幂。
def summand(n,k):
lk=log(k)
ln=log(n)
a=(lk-ln)*(k-1)
b=(log(n-k)-ln)*(2*n-k)
c=-ln
d=sum(log(x) for x in xrange(n-k+1,n+1))-sum(log(x) for x in xrange(1,k+1))
return exp(a+b+c+d)
def M(n):
return sum(summand(n,k) for k in xrange(1,n))
注意,当k = n时,summand将为零,所以我不计算它,因为对数将是未定义的。
答案 1 :(得分:3)
您可以使用gmpy2。它具有任意精度浮点运算和大指数范围。
from __future__ import division
from gmpy2 import comb,mpfr,fsum
def M(n):
return fsum(comb(n,k)*(mpfr(1)/n)*(mpfr(1)-mpfr(k)/n)**(mpfr(2)*n-k)*(mpfr(k)/n)**(k-1) for k in xrange(1,n+1))
for i in xrange(1,1000000,100):
print i,M(i)
以下是输出的摘录:
2001 0.15857490038127975
2101 0.15857582611615381
2201 0.15857666768820194
2301 0.15857743607577454
2401 0.15857814042739268
2501 0.15857878842787806
2601 0.15857938657957615
免责声明:我维持gmpy2。
答案 2 :(得分:2)
一种相当残酷的方法是计算所有因子,然后以一种结果保持在1.0左右的方式多次计算(Python 3.x):
def M(n):
return sum(summand(n, k) for k in range(1, n + 1))
def f1(n, k):
for i in range(k - 1):
yield k
for i in range(k):
yield n - i
def f2(n, k):
for i in range(k - 1):
yield 1 / n
for i in range(2 * n - k):
yield 1 - k / n
yield 1 / n
for i in range(2, k + 1):
yield 1 / i
def summand(n, k):
result = 1.0
factors1 = f1(n, k)
factors2 = f2(n, k)
while True:
empty1 = False
for factor in factors1:
result *= factor
if result > 1:
break
else:
empty1 = True
for factor in factors2:
result *= factor
if result < 1:
break
else:
if empty1:
break
return result
对于M(1101)
,我得到0.15855899364641846
,但需要几秒钟。 M(2000)
大约需要14秒,产生0.15857489065619598
。
(我确信它可以进行优化。)