对数概率的数值精度Java实现

对数概率的数值精度Java实现,java,math,probability,numerical-stability,Java,Math,Probability,Numerical Stability,有时,当您使用常用数据类型(如双精度)以非常小的概率进行计算时,数值不准确会在多次计算中级联,并导致不正确的结果。因此,建议使用,以提高数值稳定性。我已经在Java中实现了日志概率,我的实现工作正常,但是它的数值稳定性比使用原始双精度差。我的实现有什么问题?在Java中,以小概率执行多个连续计算的准确有效方法是什么 我无法对这个问题提供一个完整的演示,因为许多计算都会出现不准确的情况。然而,这里有一个问题存在的证据:由于数字的准确性,提交到CodeForces竞赛失败。运行测试#7和添加调试打印

有时,当您使用常用数据类型(如双精度)以非常小的概率进行计算时,数值不准确会在多次计算中级联,并导致不正确的结果。因此,建议使用,以提高数值稳定性。我已经在Java中实现了日志概率,我的实现工作正常,但是它的数值稳定性比使用原始双精度差。我的实现有什么问题?在Java中,以小概率执行多个连续计算的准确有效方法是什么

我无法对这个问题提供一个完整的演示,因为许多计算都会出现不准确的情况。然而,这里有一个问题存在的证据:由于数字的准确性,提交到CodeForces竞赛失败。运行测试#7和添加调试打印清楚地表明,从1774天开始,数字错误开始级联,直到概率之和下降到0(当它应该是1时)。在将Prob类替换为一个简单的double包装器之后

我的乘法概率实现:

a*b=Math.log(a)+Math.log(b)

我对添加的实施:

a+b=Math.log(a)+Math.log(1+Math.exp(Math.log(b)-Math.log(a)))

稳定性问题很可能包含在这两行中,但以下是我的整个实现:

class Prob {

        /** Math explained: https://en.wikipedia.org/wiki/Log_probability
         *  Quick start:
         *      - Instantiate probabilities, eg. Prob a = new Prob(0.75)
         *      - add(), multiply() return new objects, can perform on nulls & NaNs.
         *      - get() returns probability as a readable double */

        /** Logarithmized probability. Note: 0% represented by logP NaN. */
        private double logP;

        /** Construct instance with real probability. */
        public Prob(double real) {
            if (real > 0) this.logP = Math.log(real);
            else this.logP = Double.NaN;
        }

        /** Construct instance with already logarithmized value. */
        static boolean dontLogAgain = true;
        public Prob(double logP, boolean anyBooleanHereToChooseThisConstructor) {
            this.logP = logP;
        }

        /** Returns real probability as a double. */
        public double get() {
            return Math.exp(logP);
        }

        @Override
        public String toString() {
            return ""+get();
        }

        /***************** STATIC METHODS BELOW ********************/

        /** Note: returns NaN only when a && b are both NaN/null. */
        public static Prob add(Prob a, Prob b) {
            if (nullOrNaN(a) && nullOrNaN(b)) return new Prob(Double.NaN, dontLogAgain);
            if (nullOrNaN(a)) return copy(b);
            if (nullOrNaN(b)) return copy(a);

            double x = a.logP;
            double y = b.logP;
            double sum = x + Math.log(1 + Math.exp(y - x));
            return new Prob(sum, dontLogAgain);
        }

        /** Note: multiplying by null or NaN produces NaN (repping 0% real prob). */
        public static Prob multiply(Prob a, Prob b) {
            if (nullOrNaN(a) || nullOrNaN(b)) return new Prob(Double.NaN, dontLogAgain);
            return new Prob(a.logP + b.logP, dontLogAgain);
        }

        /** Returns true if p is null or NaN. */
        private static boolean nullOrNaN(Prob p) {
            return (p == null || Double.isNaN(p.logP));
        }

        /** Returns a new instance with the same value as original. */
        private static Prob copy(Prob original) {
            return new Prob(original.logP, dontLogAgain);
        }
    }

问题是由于在此行中使用
Math.exp(z)
的方式造成的:

a+b=Math.log(a)+Math.log(1+Math.exp(Math.log(b)-Math.log(a)))

z
达到极值时,double的数值精度不足以输出
Math.exp(z)
。这会导致我们丢失信息,产生不准确的结果,然后这些结果在多次计算中级联

z>=710时,则
Math.exp(z)=无穷大


z“我的实现工作时”:很好-继续下一步question@gpasch如果你投了反对票,请随意解释问题的症结所在。我想我已经详细解释了问题所在,并且在发布这个问题之前做了大量的研究。对不起,我误读了你的代码。我只看到了第一个构造器。很抱歉。顺便说一句,你应该在问题中展示你的全部代码。现在,您不知道问题出在哪里-因此不要只显示您怀疑的两行。您知道
a
b
的哪些值给出了不准确度吗?
double x = Math.max(a.logP, b.logP);
double y = Math.min(a.logP, b.logP);
double sum = x + Math.log(1 + Math.exp(y - x));