我想要计算类似的东西:
其中f(i)
是一个函数,可以[-1,1]
为i
中的任何{1,2,...,5000}
返回实数。
显然,总和的结果在[-1,1]
的某处,但是当我似乎无法使用直接编码在Python中计算它时,0.55000
变为0
}和comb(5000,2000)
变为inf
,这导致计算的总和变为NaN
。
所需的解决方案是使用双面登录。
这是使用身份a × b = 2log(a) + log(b)
,如果我可以计算log(a)
和log(b)
我可以计算总和,即使a
很大且b
差不多是0
。
所以我想我要问的是,如果有一种简单的计算方法
log2(scipy.misc.comb(5000,2000))
所以我可以简单地通过
计算我的总和sum([2**(log2comb(5000,i)-5000) * f(i) for i in range(1,5000) ])
@ abarnert的解决方案,在处理5000图时,通过提高计算梳子的精度来解决问题。这适用于这个例子,但不能扩展,因为如果不是5000,我们需要的内存会大大增加,例如我们有1e7。
目前,我正在使用一种丑陋的解决方法,但会降低内存消耗:
log2(comb(5000,2000)) = sum([log2 (x) for x in 1:5000])-sum([log2 (x) for x in 1:2000])-sum([log2 (x) for x in 1:3000])
有可能以可读的表达方式这样做吗?
答案 0 :(得分:8)
总和
是f
对n = 5000
和p = 0.5
的{{3}}的期望。
您可以使用binomial distribution来计算:
import scipy.stats as stats
def f(i):
return i
n, p = 5000, 0.5
print(stats.binom.expect(f, (n, p), lb=0, ub=n))
# 2499.99999997
另请注意,当n
变为无穷大且p
已修复时,二项分布scipy.stats.binom.expect的平均值为np
和方差np*(1-p)
。因此,对于较大的n
,您可以改为计算:
import math
print(stats.norm.expect(f, loc=n*p, scale=math.sqrt((n*p*(1-p))), lb=0, ub=n))
# 2500.0
答案 1 :(得分:3)
默认情况下,comb
会为您提供float64
,它会溢出并为您提供inf
。
但是如果你传递exact=True
,它会给你一个Python变量大小的int
,它不会溢出(除非你的内存耗尽这么荒谬)。
而且,虽然您无法在np.log2
上使用int
,但您可以使用Python的math.log2
。
所以:
math.log2(scipy.misc.comb(5000, 2000, exact=True))
作为替代方案,您选择k的亲戚定义为n!k / k!
,对吗?您可以将其减少到∏(i=1...k)((n+1-i)/i)
,这很容易计算。
或者,如果您想避免溢出,可以通过交替* (n-i)
和/ (k-i)
来实现。
当然,您还可以减少添加和减去日志。我认为在Python中循环并计算4000个对数比在C中循环并计算4000次乘法要慢,但我们总是可以对其进行矢量化,然后,它可能会更快。让我们写下来并测试:
In [1327]: n, k = 5000, 2000
In [1328]: %timeit math.log2(scipy.misc.comb(5000, 2000, exact=True))
100 loops, best of 3: 1.6 ms per loop
In [1329]: %timeit np.log2(np.arange(n-k+1, n+1)).sum() - np.log2(np.arange(1, k+1)).sum()
10000 loops, best of 3: 91.1 µs per loop
当然,如果你更关心记忆而不是时间......好吧,这显然会让情况变得更糟。我们一次有2000个8字节浮点数而不是一个608字节整数。如果你达到100000,20000,你得到20000个8字节浮点数而不是一个9K整数。在1000000,200000,它是200000个8字节浮点数与一个720K整数。
我不确定为什么这两种方式对你来说都是一个问题。特别是考虑到你使用的是listcomp而不是genexpr,因此创建一个不必要的5000,100000或1000000 Python浮动列表--24MB不是问题,但是720K是?但如果是这样,我们显然可以迭代地做同样的事情,代价是速度:
r = sum(math.log2(n-i) - math.log2(k-i) for i in range(n-k))
这不是太比scipy
解决方案慢得多,而且它永远不会使用超过一小块常量字节(少数Python浮点数)。 (除非你使用的是Python 2,在这种情况下......只需使用xrange
代替range
,它就会恢复为常量。)
作为旁注,为什么 你使用列表推导而不是带矢量化操作的NumPy数组(速度,还有一点紧凑性)或生成器表达式而不是列表理解(根本没有内存使用,无需加速)?
答案 2 :(得分:3)
编辑:@unutbu已经回答了真正的问题,但是我会留在这里,以防log2comb(n, k)
对任何人都有用。
comb(n, k)
是n! /((n-k)!k!)和n!可以使用Gamma function gamma(n+1)
计算。 Scipy提供函数scipy.special.gamma
。 Scipy还提供gammaln
,这是Gamma函数的日志(自然日志)。
因此log(comb(n, k))
可以计算为gammaln(n+1) - gammaln(n-k+1) - gammaln(k+1)
例如,log(comb(100,8))(执行from scipy.special import gammaln
之后):
In [26]: log(comb(100, 8))
Out[26]: 25.949484949043022
In [27]: gammaln(101) - gammaln(93) - gammaln(9)
Out[27]: 25.949484949042962
和log(comb(5000,2000)):
In [28]: log(comb(5000, 2000)) # Overflow!
Out[28]: inf
In [29]: gammaln(5001) - gammaln(3001) - gammaln(2001)
Out[29]: 3360.5943053174142
(当然,要获得基数为2的对数,只需除以log(2)
。)
为方便起见,您可以定义:
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)