概率很小的准确性
Accuracy with very small probabilities
我在 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' 值,因为这些是我到目前为止得到的。
我认为您应该尝试使用 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;
}
我在 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' 值,因为这些是我到目前为止得到的。
我认为您应该尝试使用 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;
}