Handle summation of big binomials in Python

为君一笑 提交于 2021-02-10 06:01:38

问题


I need to compute this formula:

It is an approximation of this integral

but it doesn't matter, actually I just want to compute the value of Figure 1 with PYTHON, that's what the topic concerns.

K, alpha and sigma are fixed values within a single computation, usually:

  • 0 <= k <= 99;
  • alpha = 3;
  • sigma = 2.

Below is how I am trying to compute such summation in python:

import decimal
from scipy.special import binom

def residual_time_mean(alpha, sigma=2, k=1):
    prev_prec = decimal.getcontext().prec
    D = decimal.Decimal
    decimal.getcontext().prec = 128

    a = float(alpha)
    s = float(sigma)
    sum1 = 0.0
    sum2 = 0.0
    sumD1 = D(0.0)
    sumD2 = D(0.0)

    for i in range(1, k + 1):
        sum1 += binom(k, i) * ((-1) ** (i + 1)) * (s / ((a - 1) * i - 1.0))
        sum2 += binom(k, i) * ((-1) ** (i + 1)) * s / ((a - 1) * i - 1.0)
        sumD1 += D(binom(k, i)) * (D(-1.0) ** (D(i) + D(1.0))) * (D(s) / ((D(a) - D(1.0)) * D(i) - D(1.0)))
        sumD2 += D(binom(k, i)) * (D(-1.0) ** (D(i) + D(1.0))) * D(s) / ((D(a) - D(1.0)) * D(i) - D(1.0))

    decimal.getcontext().prec = prev_prec

    return sum1, sum2, float(sumD1), float(sumD2)

Running

for k in [0, 1, 2, 4, 8, 20, 50, 99]:
    print("k={} -> {}".format(k, residual_time_mean(3, 2, k)))

the outcome is:

k=0 -> (0.0, 0.0, 0.0, 0.0)
k=1 -> (2.0, 2.0, 2.0, 2.0)
k=2 -> (3.3333333333333335, 3.3333333333333335, 3.3333333333333335, 3.3333333333333335)
k=4 -> (5.314285714285714, 5.314285714285714, 5.314285714285714, 5.314285714285714)
k=8 -> (8.184304584304588, 8.184304584304583, 8.184304584304584, 8.184304584304584)
k=20 -> (13.952692275798238, 13.952692275795965, 13.95269227579524, 13.95269227579524)
k=50 -> (23.134878809207617, 23.13390225415814, 23.134078892910786, 23.134078892910786)
k=99 -> (265412075330.96634, 179529505602.9507, 17667813427.20196, 17667813427.20196)

You can see that starting from k=8 the results are different.

Making a multiplication before a division leads results of sum1 and sum2 to diverge a lot for k=99 for instance.

sum1 += binom(k, i) * ((-1) ** (i + 1)) * (s / ((a - 1) * i - 1.0))
sum2 += binom(k, i) * ((-1) ** (i + 1)) * s / ((a - 1) * i - 1.0)

With decimal this problem doesn't occur but the result is not correct at all.

Computing the summation on WolframAlpha

for k = 99

(Here is the link for the computation on WolframAlpha). It gives 33.3159488(...) while for my python function it is 17667813427.20196. I trust WolframAlpha since it makes something like symbolic computation, indeed it also returns the real value in form of a fraction.

for other k

Approximation problems (e.g. the value computed by Wolfram is different from the one computed in python by an order of magnitude of 10^0 or more) starts occurring from k~=60.

Moreover computing the integral (Figure 2) with scipy.integrate leads to similar approximation errors.

The question:

Do you have any suggestion to handle this computation? Increasing decimal precision doesn't seem to be helpful.


回答1:


I don't understand why you involve a numpy function here, and why you are converting to float objects. Really, for this formula, if your inputs are always integers, then simply stick with int and fractions.Fraction and your answers will always be exact. It is easy enough to implement your own binom function:

In [8]: def binom(n, k):
    ...:     return (
    ...:         factorial(n)
    ...:         // (factorial(k)*factorial(n-k))
    ...:     )
    ...:

Note, I used integer division: //. And finally, your summation:

In [9]: from fractions import Fraction
    ...: def F(k, a, s):
    ...:     result = Fraction(0, 1)
    ...:     for i in range(1, k+1):
    ...:         b = binom(k, i)*pow(-1, i+1)
    ...:         x = Fraction(s, (a-1)*i - 1)
    ...:         result += b*x
    ...:     return result
    ...:

And the results:

In [10]: F(99, 3, 2)
Out[10]: Fraction(47372953498165579239913571130715220654368322523193013011418, 1421930192463933435386372127473055337225260516259631545875)

Which seems correct based on wolfram-alpha...

Note, if say, alpha can be a non-integer, you could use decimal.Decimal for arbitary-precision floating point operations:

In [17]: from decimal import Decimal
     ...: def F(k, a, s):
     ...:     result = Decimal('0')
     ...:     for i in range(1, k+1):
     ...:         b = binom(k, i)*pow(-1, i+1)
     ...:         x = Decimal(s) / Decimal((a-1)*i - 1)
     ...:         result += b*x
     ...:     return result
     ...:

In [18]: F(99, 3, 2)
Out[18]: Decimal('33.72169506311642881389682714')

Let's up the precision:

In [20]: import decimal

In [21]: decimal.getcontext().prec
Out[21]: 28

In [22]: decimal.getcontext().prec = 100

In [23]: F(99, 3, 2)
Out[23]: Decimal('33.31594880623309576443774363783112352607484321721989160481537847749994248174570647797323789728798446')



回答2:


I've discovered the problem by myself:

Executing scipy.special.binom(99,50) gives

5.044567227278209e+28

while calculating binomial (99,50) on WolframAlpha gives

5.0445672272782096667406248628e+28

There is an absolute difference with an order of magnitude of 10^12.

That's why, for sure, results of python function are unreliable for high values of k. So I need to change how the binomial is computed.



来源:https://stackoverflow.com/questions/50601513/handle-summation-of-big-binomials-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!