问题
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