Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 如何有效地计算二项累积分布函数?_Algorithm_Math_Probability_Binomial Cdf - Fatal编程技术网

Algorithm 如何有效地计算二项累积分布函数?

Algorithm 如何有效地计算二项累积分布函数?,algorithm,math,probability,binomial-cdf,Algorithm,Math,Probability,Binomial Cdf,假设我知道“成功”的概率是p。我运行测试N次,我看到s成功。这个测试类似于投掷一枚重量不均的硬币(也许正面是成功的,反面是失败的) 我想知道看到S成功的大概概率,或者看到一些比S成功可能性小的成功 例如,如果p是0.3,N是100,我获得20次成功,我在寻找获得20次或更少成功的概率 另一方面,如果p是0.3,N是100,我获得了40次成功,我在寻找获得40次以上成功的可能性 我知道这个问题与寻找二项曲线下的面积有关,但是: 我的数学能力不足以将这些知识转化为有效的代码 虽然我知道二项式曲线会给

假设我知道“成功”的概率是p。我运行测试N次,我看到s成功。这个测试类似于投掷一枚重量不均的硬币(也许正面是成功的,反面是失败的)

我想知道看到S成功的大概概率,或者看到一些比S成功可能性小的成功

例如,如果p是0.3,N是100,我获得20次成功,我在寻找获得20次或更少成功的概率

另一方面,如果p是0.3,N是100,我获得了40次成功,我在寻找获得40次以上成功的可能性

我知道这个问题与寻找二项曲线下的面积有关,但是:

  • 我的数学能力不足以将这些知识转化为有效的代码
  • 虽然我知道二项式曲线会给出精确的结果,但我得到的印象是,它本质上是低效的。一种计算近似结果的快速方法就足够了
  • 我应该强调,这种计算必须是快速的,并且在理想情况下,应该可以通过标准的64位或128位浮点计算来确定


    我在寻找一个函数,它取p,S,N,并返回一个概率。因为我对代码比对数学符号更熟悉,所以我更喜欢任何答案使用伪代码或代码。

    试试,在GMP中使用。另一个参考是。

    我想你应该评估一下


    在“C中的数字配方”第6章:“特殊函数”中有一个很好的使用连分数表示的实现。

    从您的问题“至少得到s个头”部分,您需要累积二项分布函数。请参见方程,该方程被描述为“正则化不完全贝塔函数”(已回答)。如果您只想计算答案而不必自己实现整个解决方案,GNU科学库提供了以下功能:gsl_cdf_二项式和gsl_cdf_二项式。

    在计算机辅助设计中使用的贝塞尔曲线域中存在一种高效且更重要的数值稳定算法。它被称为de Casteljau算法,用于计算用于定义Bezier曲线的Bernstein多项式

    我相信每个答案只允许我有一个链接,所以从

    注意二项分布和伯恩斯坦多项式之间的密切关系。然后点击de Casteljau算法的链接

    假设我知道用一个特定的硬币掷人头的概率是p。 我投掷的概率是多少 硬币T倍,并且至少得到 谁的头

    • 集合n=T
    • 为i=0设置beta[i]=0。。。S-1
    • 为i=S设置beta[i]=1。。。T
    • 设置t=p
    • 使用de Casteljau评估B(t)
    或者至多是一个女人的脑袋

    • 集合n=T
    • 将β[i]=1设置为i=0。。。
    • 为i=S+1设置beta[i]=0。。。T
    • 设置t=p
    • 使用de Casteljau评估B(t)

    开源代码可能已经存在。NURBS曲线(非均匀有理B样条曲线)是Bezier曲线的推广,在CAD中得到广泛应用。尝试openNurbs(许可证非常自由)或失败的开放级联(不太自由和不透明的许可证)。两个工具包都是C++的,但是,Irc,.NET绑定存在。 精确二项分布

    def factorial(n): 
        if n < 2: return 1
        return reduce(lambda x, y: x*y, xrange(2, int(n)+1))
    
    def prob(s, p, n):
        x = 1.0 - p
    
        a = n - s
        b = s + 1
    
        c = a + b - 1
    
        prob = 0.0
    
        for j in xrange(a, c + 1):
            prob += factorial(c) / (factorial(j)*factorial(c-j)) \
                    * x**j * (1 - x)**(c-j)
    
        return prob
    
    >>> prob(20, 0.3, 100)
    0.016462853241869437
    
    >>> 1-prob(40-1, 0.3, 100)
    0.020988576003924564
    
    import math
    def erf(z):
            t = 1.0 / (1.0 + 0.5 * abs(z))
            # use Horner's method
            ans = 1 - t * math.exp( -z*z -  1.26551223 +
                                                    t * ( 1.00002368 +
                                                    t * ( 0.37409196 + 
                                                    t * ( 0.09678418 + 
                                                    t * (-0.18628806 + 
                                                    t * ( 0.27886807 + 
                                                    t * (-1.13520398 + 
                                                    t * ( 1.48851587 + 
                                                    t * (-0.82215223 + 
                                                    t * ( 0.17087277))))))))))
            if z >= 0.0:
                    return ans
            else:
                    return -ans
    
    def normal_estimate(s, p, n):
        u = n * p
        o = (u * (1-p)) ** 0.5
    
        return 0.5 * (1 + erf((s-u)/(o*2**0.5)))
    
    >>> normal_estimate(20, 0.3, 100)
    0.014548164531920815
    
    >>> 1-normal_estimate(40-1, 0.3, 100)
    0.024767304545069813
    
    泊松估计:适用于大n和小p

    import math
    
    def poisson(s,p,n):
        L = n*p
    
        sum = 0
        for i in xrange(0, s+1):
            sum += L**i/factorial(i)
    
        return sum*math.e**(-L)
    
    >>> poisson(20, 0.3, 100)
    0.013411150012837811
    >>> 1-poisson(40-1, 0.3, 100)
    0.046253037645840323
    
    具有C#函数(围绕C代码的包装)来评估许多CDF函数,包括二项式分布。您可以找到原始的C和FORTRAN代码。这段代码经过了很好的测试,非常准确


    如果您想编写自己的代码以避免依赖外部库,可以使用其他答案中提到的二项式的法线近似值。以下是在各种情况下的一些注意事项。如果你走这条路,需要代码来计算正常的CDF,这里有一些方法。它只有十几行代码,可以很容易地移植到任何其他语言。但如果您想要高精度和高效的代码,最好使用第三方代码,如DCDFLIB。制作那个图书馆花了好几年时间。

    我不能完全保证效率,但Scipy有一个


    如果您使用的是Python,则无需自己编写代码。Scipy让你得到了保障:

    from scipy.stats import binom
    # probability that you get 20 or less successes out of 100, when p=0.3
    binom.cdf(20, 100, 0.3)
    >>> 0.016462853241869434
    
    # probability that you get exactly 20 successes out of 100, when p=0.3
    binom.pmf(20, 100, 0.3)
    >>> 0.0075756449257260777
    

    我参与了一个项目,我们需要能够在没有定义阶乘或伽马函数的环境中计算二项式CDF。这花了我几个星期的时间,但我最终想出了下面的算法,可以精确地计算CDF(也就是说,不需要近似值)。Python基本上和伪代码一样好,对吗

    import numpy as np
    
    def binomial_cdf(x,n,p):
        cdf = 0
        b = 0
        for k in range(x+1):
            if k > 0:
                b += + np.log(n-k+1) - np.log(k) 
            log_pmf_k = b + k * np.log(p) + (n-k) * np.log(1-p)
            cdf += np.exp(log_pmf_k)
        return cdf
    
    性能等级为x。对于较小的x值,此解决方案比scipy.stats.binom.cdf快约一个数量级,在x=10000左右时具有类似的性能

    由于stackoverflow不支持MathJax,因此我不会对该算法进行完整的推导,但其重点是首先确定以下等价性:

    • 对于所有的k>0,
      sp.misc.comb(n,k)=np.prod([(n-k+1)/k表示范围(1,k+1)])
    我们可以将其改写为:

    • sp.misc.comb(n,k)==sp.misc.comb(n,k-1)*(n-k+1)/k
    或在日志空间中:

    • np.log(sp.misc.comb(n,k))==np.log(sp.misc.comb(n,k-1))+np.log(n-k+1)-np.log(k)
    因为CDF是PMF的总和,我们可以使用这个公式从我们为PMF_{x=i-1}计算的系数计算PMF_{x=i}的二项式系数(在上面的函数中,它的对数是
    b
    )。这意味着我们可以使用accum在单个循环内完成所有操作
    import numpy as np
    
    def binomial_cdf(x,n,p):
        cdf = 0
        b = 0
        for k in range(x+1):
            if k > 0:
                b += + np.log(n-k+1) - np.log(k) 
            log_pmf_k = b + k * np.log(p) + (n-k) * np.log(1-p)
            cdf += np.exp(log_pmf_k)
        return cdf
    
    import numpy as np
    np.random.seed(1)
    x=np.random.binomial(20,0.6,10000) #20 flips of coin,probability of 
                                     heads percentage and 10000 times 
                                      done.
    sum(x>12)/len(x)
    
    The output is 41% of times we got 12 heads.