【问题标题】:Accuracy with very small probabilities概率很小的精度
【发布时间】:2017-08-02 17:40:52
【问题描述】:

我用 Java 编写了一个程序,它需要我计算一些概率,对于较大的输入,概率最终会变得非常小。因此,为了防止下溢问题,我想取而代之的是日志概率。

但是,我在执行此操作时遇到了麻烦。在计算的每个阶段,可以有不同数量的选项,需要为其分配概率,并且每个阶段它们需要加起来为 1。概率基于许多不同的变量。我使用以下公式对所有可能性求和:

Math.pow(d[i], a) * Math.pow(1/c[i], b)

这给了我一个变量total。然后建立概率p_i,

p_i = (Math.pow(d[i], a) * Math.pow(1/c[i], b)) / total

我的问题是,如何使用对数概率来实现这一点,这样我就不会得到“Infinity”和“NaN”值,因为这些是我到目前为止所得到的。

【问题讨论】:

  • 不要以为求和的对数可以化简
  • 那么,是不是说在这里实现日志概率是不可能的?
  • 为什么不使用 BigDecimal?
  • 您可能需要切换到使用 BigDecimal,因为它允许任意精度。
  • 我倾向于你必须使用BigDecimal,但不幸的是,我认为你必须使用roll your own 对数函数BigDecimal。没有内置的惊人...

标签: java math probability


【解决方案1】:

我认为您应该尝试使用Kahan Summation。它将允许正确求和而不会丢失精度。

在一些类似 C 的伪代码中(对不起,我的 Java 生锈了,代码未经测试)

double total(int N, double[] d, double[] c, double a, double b) {

    double sum           = 0.0;
    double running_error = 0.0;

    for (int i = 0; i != N; ++i) {
        if (d[i] == 0.0)
            continue;

        if (c[i] == 0.0)
            throw "XXX"; // some error reporting

        double v = 0.0;
        if (d[i] > 0.0 && c[i] > 0.0) {
            // using log trick, if you want
            double lpi = a*Math.log(d[i]) - b*Math.log(c[i]);
            v = Math.exp(lpi);   
        }
        else {
            v = Math.pow(d[i], a) * Math.pow(1.0/c[i], b);
        }

        double difference = v - running_error;
        double temp = sum + difference;
        running_error = (temp - sum) - difference;
        sum = temp;
     }
     return sum;
}

【讨论】:

    猜你喜欢
    • 2020-06-22
    • 2011-12-01
    • 2012-12-07
    • 2022-01-14
    • 1970-01-01
    • 2012-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多