如何可靠地添加大指数项而不会出现溢出错误?


24

蒙特卡洛马尔可夫链中一个非常普遍的问题涉及计算概率,这些概率是大指数项的和,

ea1+ea2+...

其中的各组分可以从非常小的范围内,以非常大的。我的方法是排除最大指数项以便:aK:=maxi(ai)

a=K+log(ea1K+ea2K+...)
eaea1+ea2+...

这种做法是合理的,如果所有的元素a,如果他们不是很大,但不是一个好主意。当然,较小的元素无论如何都不会对浮点数求和,但是我不确定如何可靠地处理它们。在R代码中,我的方法如下所示:

if ( max(abs(a)) > max(a) )
  K <-  min(a)
else
  K <- max(a)
ans <- log(sum(exp(a-K))) + K

应该有一个标准的解决方案,这似乎是一个很常见的问题,但是我不确定它是什么。感谢您的任何建议。


1
这是一回事。Google的“ logsumexp”。

Answers:


15

有一个简单的解决方案,只有两次通过数据:

首先计算

K:=maxiai,

它告诉您,如果有项,则 n

ieaineK.

由于您大概没有甚至比,因此您不必担心 中会出现双精度溢出的情况。 。n1020

τ:=ieaiKn

因此,计算,然后您的解决方案就是。τeKτ


感谢您的明确表示法-但我相信这本质上是我所建议的(?)如果在很小的情况下需要避免下溢错误,那么我需要@gareth提出的Kahan求和方法?ai
cboettig'2

啊,我现在明白你的意思了。实际上,您不必担心下溢,因为在解决方案中添加异常微小的结果不会改变它。如果它们的数量过多,则应首先将较小的值求和。
Jack Poulson

致低落的投票者:您介意让我知道我的答案有什么问题吗?
Jack Poulson

如果您有很多非常小的用语怎么办?这些可能会发生。如果有很多这样的术语,您将有一个大错误。eaiK0
becko


10

为了保持精确度,同时添加双精度数时,您需要使用Kahan Summation,该软件等效于带有进位寄存器的软件。

这对于大多数值来说都很好,但是如果您溢出,那么您将达到IEEE 754双精度的极限,该极限约为。此时,您需要一个新的表示形式。您可以在加法时检测到溢出,也可以通过检测大的指数来进行评估。此时,您可以通过移动指数并跟踪此移动来修改双精度型的解释。e709.783doubleMax - sumSoFar < valueToAddexponent > 709.783

大多数情况下,这与您的指数补偿方法相似,但是此版本将其保留在基数2中,不需要进行初始搜索即可找到最大的指数。因此。value×2shift

#!/usr/bin/env python
from math import exp, log, ceil

doubleMAX = (1.0 + (1.0 - (2 ** -52))) * (2 ** (2 ** 10 - 1))

def KahanSumExp(expvalues):
  expvalues.sort() # gives precision improvement in certain cases 
  shift = 0 
  esum = 0.0 
  carry = 0.0 
  for exponent in expvalues:
    if exponent - shift * log(2) > 709.783:
      n = ceil((exponent - shift * log(2) - 709.783)/log(2))
      shift += n
      carry /= 2*n
      esum /= 2*n
    elif exponent - shift * log(2) < -708.396:
      n = floor((exponent - shift * log(2) - -708.396)/log(2))
      shift += n
      carry *= 2*n
      esum *= 2*n
    exponent -= shift * log(2)
    value = exp(exponent) - carry 
    if doubleMAX - esum < value:
      shift += 1
      esum /= 2
      value /= 2
    tmp = esum + value 
    carry = (tmp - esum) - value 
    esum = tmp
  return esum, shift

values = [10, 37, 34, 0.1, 0.0004, 34, 37.1, 37.2, 36.9, 709, 710, 711]
value, shift = KahanSumExp(values)
print "{0} x 2^{1}".format(value, shift)

Kahan求和只是“补偿求和”方法家族中的一种。如果出于某种原因,Kahan的工作不太正确,则可以使用许多其他方法适当地将幅度和相反符号的项相加。
2012年

@JM您能为我提供其他方法的名称吗,我很想读它们。谢谢。
Gareth A. Lloyd

1

您的方法是可靠的。

您无需确切地知道,就足以避免溢出。因此,您可以在进行任何MCMC采样之前通过分析来估计KK


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.