我的问题非常简单。我想计算以下总和。
from __future__ import division
from scipy.misc import comb
import math
for n in xrange(2,1000,10):
m = 2.2*n/math.log(n)
print sum(sum(comb(n,a) * comb(n-a,b) * (comb(a+b,a)*2**(-a-b))**m
for b in xrange(n+1))
for a in xrange(1,n+1))
然而,python提供了RuntimeWarning: overflow encountered in multiply
和nan
作为输出,它也非常慢。
有一种聪明的方法吗?
答案 0 :(得分:16)
你得到NaNs的原因是你最终会评估像
这样的数字comb(600 + 600, 600) == 3.96509646226102e+359
这太大而不适合浮点数:
>>> numpy.finfo(float).max
1.7976931348623157e+308
采用对数来避免它:
from __future__ import division, absolute_import, print_function
from scipy.special import betaln
from scipy.misc import logsumexp
import numpy as np
def binomln(n, k):
# Assumes binom(n, k) >= 0
return -betaln(1 + n - k, 1 + k) - np.log(n + 1)
for n in range(2, 1000, 10):
m = 2.2*n/np.log(n)
a = np.arange(1, n + 1)[np.newaxis,:]
b = np.arange(n + 1)[:,np.newaxis]
v = (binomln(n, a)
+ binomln(n - a, b)
+ m*binomln(a + b, a)
- m*(a+b) * np.log(2))
term = np.exp(logsumexp(v))
print(term)
答案 1 :(得分:1)
使用Memoize模式。有了它,重新定义梳子:
@memoized
def newcomb(a, b):
return comb(a, b)
并将comb
的所有来电替换为newcomb
。此外,为了进行微小改进,请移除括号。如果您制作明确的列表,则会浪费时间构建它们。如果您将其删除,则可以有效地使用generator expressions。
<强>更新强>:
这不会解决nan
问题,但确实会让它更快。
对于那些不认为速度更快的人,您是否应用了memoize装饰器?在我的机器上,原始功能需要29.7秒才能达到200,而备忘录版本只需3.8秒。
memoize所做的只是将comb
的所有调用存储在查找表中。因此,如果在稍后的迭代中,您使用与过去某个时刻相同的参数调用comb
,则不会重新计算它 - 它只是在查找表中查找它。