Wie fügt man zuverlässig große Exponentialterme ohne Überlauffehler hinzu?


24

Ein sehr verbreitetes Problem in der Markov-Kette von Monte Carlo ist die Berechnung von Wahrscheinlichkeiten, die sich aus großen Exponentialausdrücken zusammensetzen.

ea1+ea2+...

wobei die Komponenten a Dose von sehr klein bis sehr groß reichen. Mein Ansatz war es, den größten exponentiellen Term so dass:K:=maxi(ai)

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

Dieser Ansatz ist sinnvoll, wenn alle Elemente von ein groß sind, aber keine so gute Idee, wenn sie nicht groß sind. Natürlich tragen die kleineren Elemente sowieso nicht zur Gleitkommasumme bei, aber ich bin mir nicht sicher, wie ich mit ihnen zuverlässig umgehen soll. In R-Code sieht mein Ansatz folgendermaßen aus:

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

Es scheint ein weit verbreitetes Problem zu sein, dass es eine Standardlösung geben sollte, aber ich bin mir nicht sicher, was es ist. Vielen Dank für alle Vorschläge.


1
Das ist eine Sache. Google für 'logsumexp'.

Antworten:


15

Es gibt eine einfache Lösung mit nur zwei Durchgängen durch die Daten:

Berechne zuerst

K:=maxiai,

die besagt, dass, wenn es Begriffe, dann Σ i e a in e K .n

ieaineK.

Da Sie vermutlich annähernd so groß wie 10 20 haben , sollten Sie sich keine Gedanken über ein Überlaufen bei der Berechnung von τ : = i e a i - Kn mit doppelter Genauigkeit machen.n1020

τ:=ieaiKn

Berechne also und dann ist e K τ deine Lösung .τeKτ


ai

Ah, ich sehe jetzt, worauf du hinaus willst. Sie müssen sich eigentlich keine Gedanken über Unterlauf machen, da das Hinzufügen von außergewöhnlich kleinen Ergebnissen zu Ihrer Lösung dies nicht ändern sollte. Wenn es eine außergewöhnlich große Anzahl von ihnen gab, sollten Sie zuerst die kleinen Werte summieren.
Jack Poulson

An den Abwähler: Würde es Ihnen etwas ausmachen, mich wissen zu lassen, was mit meiner Antwort nicht stimmt?
Jack Poulson

eaiK0


10

Um die Genauigkeit beim Addieren von Doppelwerten zu erhalten, müssen Sie Kahan Summation verwenden . Dies ist die Software, die einem Carry-Register entspricht.

e709.783doubleMax - sumSoFar < valueToAddexponent > 709.783

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)

Die Kahan-Summation ist nur eine von mehreren Methoden der "kompensierten Summation". Wenn Kahan aus irgendeinem Grund nicht richtig funktioniert, gibt es eine Reihe anderer Methoden, um Terme unterschiedlicher Größen und entgegengesetzter Vorzeichen richtig zu addieren.
JM,

@JM Könnten Sie mir die Namen dieser anderen Methoden mitteilen, ich wäre sehr daran interessiert, sie einzulesen. Vielen Dank.
Gareth A. Lloyd


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.