超几何分布不能正常工作java

时间:2016-09-29 00:48:28

标签: java android math

使用超几何分布来计算概率我在math.stackexchange中得到了一些帮助,但我在java中计算这个有问题

我不是这方面的专家,我一直想弄清楚没有运气,我知道问题出在我的java代码上,因为当我在我的计算器上尝试公式时,我得到了正确的结果,在这种情况下是0.364

这是公式

enter image description here

二分法以这种方式计算

(K k) = K! / ((K - k)! * k!)

这就是我试图在java代码中复制它的方式

 public static void main(String[] args){
    BigDecimal[] prob = HyperGeometricDistribution(20,2,50,5);
    String _prob = prob[0] + "." +  prob[1];
    System.out.println(_prob);
}

private static BigDecimal fact(BigDecimal n) {
    BigDecimal result = BigDecimal.ONE;
    while (!n.equals(BigDecimal.ZERO)) {
        result = result.multiply(n);
        n = n.subtract(BigDecimal.ONE);
    }
    return result;
}

private static BigDecimal Binomial(int a, int b) {
    return fact(BigDecimal.valueOf(a)).divide(fact(BigDecimal.valueOf(a-b)).multiply(fact(BigDecimal.valueOf(b))), BigDecimal.ROUND_DOWN);
}

// K : Number of Successes in Population
// k : number of Successes in Sample
// N : Population Size
// m : Sample Size
private static BigDecimal[] HyperGeometricDistribution(int K,int k, int N, int m){
    return (Binomial(K,k).multiply(Binomial(N-K,m-k))).divideAndRemainder(Binomial(N,m));
}

这将打印0.77但正确的答案是0.364 Online Example

我将在android中使用它,我将传递的最大值 N 是3910我需要使用BigDecimals,因为我需要计算3910!这是一个巨大的数字。

感谢任何帮助

1 个答案:

答案 0 :(得分:1)

divideAndRemainder()的其余部分不是十进制表示的小数部分。你需要将余数除以实际除数。

但实际上,您不需要BigDecimal。你只需要一种聪明的计算方法。让我们检查二项式系数的公式,例如n=5, k=3。然后:

c(5, 3) = (5 * 4 * 3 * 2 * 1) / ((2 * 1) * (3 * 2 * 1))

如您所见,(n-k)!部分完全取消,最后以

结束
c(5, 3) = (5 * 4 * 3) / (3 * 2 * 1)
        = 5/3 * 4/2 * 3/1

所以你只需要乘以k分数。如果你总是减少分数,你将不需要非常大的数字。事实上,超几何分布的计算也是一个分数,你可以再次减少事情以保持数字较小。这是一些执行此操作的C#代码。它没什么特别的,几乎完全转化为Java:

static class GCDHelper
{
    public static long GCD(long a, long b)
    {
        while(b != 0)
        {
            var temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
}

class Fraction
{
    public long Numerator;
    public long Denominator;

    public Fraction(long numerator, long denominator)
    {
        this.Numerator = numerator;
        this.Denominator = denominator;
    }

    public void Reduce()
    {
        var gcd = GCDHelper.GCD(Numerator, Denominator);
        Numerator /= gcd;
        Denominator /= gcd;
    }

    public double ToNumber() { return (double)Numerator / Denominator; }
}

class Program
{
    static void MultiplyBinomialCoefficient(int n, int k, bool inverse, Fraction f)
    {
        if (k > n / 2)
            k = n - k;
        for(int i = 1; i <= k; ++i)
        {
            if (!inverse)
            {
                f.Numerator *= n - i + 1;
                f.Denominator *= i;
            }
            else
            {
                f.Denominator *= n - i + 1;
                f.Numerator *= i;
            }
            f.Reduce();
        }
    }

    static double Hypergeometric(int K, int k, int N, int m)
    {
        var f = new Fraction(1, 1);
        MultiplyBinomialCoefficient(K, k, false, f);
        MultiplyBinomialCoefficient(N - K, m - k, false, f);
        MultiplyBinomialCoefficient(N, m, true, f);
        return f.ToNumber();
    }

    static void Main(string[] args)
    {
        Console.WriteLine(Hypergeometric(20, 2, 50, 5));
    }
}

结果:

0.364080877494384

还有一些参数组合让数字溢出。幸运的是,您只需将long变量替换为BigInteger变量(并相应地调整运算符)。其余的应该可以正常工作。