4
\$\begingroup\$

The below code was written to generate γ, for educational purposes.

Single threaded, no functional zeroes required, no binary splitting(which can all be used to compute competitively like y-cruncher, that version in works). Uses the Arithmetic Geometric Mean to calculate large logarithms quickly. Uses decimal module for precision management.

I've computed 3000 digits in a few hours with it, and 200 in about a minute. I am happy calculating.

import decimal
D = decimal.Decimal
def agm(a, b): #Arithmetic Geometric Mean
 a, b = D(a),D(b)
 for x in range(prec):
 a, b = (a + b) / 2, (a * b).sqrt()
 return a
def pi_agm(): #Pi via AGM and lemniscate
 a, b, t, p, pi, k = 1, D(2).sqrt()/2, 1/D(2), 2, 0, 0
 while 1:
 an = (a + b) / 2
 b = (a * b).sqrt()
 t -= p * (a - an)**2
 a, p = an, 2**(k+2)
 piold = pi
 pi = (a + b) * (a + b) / (2*t)
 k += 1
 if pi == piold:
 break
 return pi
def factorial(x): #factorial fast loop
 x = int(x)
 factorial = D(1)
 for i in range(1, x+1):
 factorial *= i
 return factorial
def lntwo(): #Fast converging Ln 2
 logsum, logold, n = D(0), D(0), 0
 while 1:
 logold = logsum
 logsum += D(1/((D(961**n))*((2*n)+1)))
 n += 1
 if logsum == logold:
 logsum1 = (D(14)/D(31))*logsum
 break
 logsum, logold, n = D(0), D(0), 0
 while 1:
 logold = logsum
 logsum += D(1/((D(25921**n))*((2*n)+1)))
 n += 1
 if logsum == logold:
 logsum2 = (D(6)/D(161))*logsum
 break
 logsum, logold, n = D(0), D(0), 0
 while 1:
 logold = logsum
 logsum += D(1/((D(2401**n))*((2*n)+1)))
 n += 1
 if logsum == logold:
 logsum3 = (D(10)/D(49))*logsum
 break
 ln2 = logsum1 + logsum2 + logsum3
 return ln2
def lnagm(x): #Natural log via AGM,
 try:
 if int(x) == 1:
 return 0
 if int(x) == 2:
 return lntwo()
 except:
 pass
 m = prec*2
 ln2 = lntwo()
 decimal.getcontext().prec = m
 pi = D(pi_agm())
 twoprec = D(2**(2-D(m)))/D(x)
 den = agm(1, twoprec)*2
 diff = m*ln2
 result = (D(pi/den) - D(diff))
 logr = D(str(result)[:m//2])
 decimal.getcontext().prec = prec
 return logr
def gamma(): #Compute Gamma from Digamma Expansion
 print('Computing Gamma!')
 k = D(prec/2)
 print('Calculating Logarithms...')
 lnk = lnagm(k)
 logsum = D(0)
 upper = int((12*k)+2)
 print('Summing...')
 for r in range(1, upper):
 logsum += D((D(-1)**D(r-1))*D(k**D(r+1)))/D(factorial(r-1)*D(r+1))
 if r%1000==0:
 print(str((D(r)/D(upper))*100)[:5], '% ; Sum 1 of 2')
 logsum1 = D(0)
 print('...')
 for r in range(1, upper):
 logsum1 += D((D(-1)**D(r-1))*(k**D(r+1)))/D(factorial(r-1)*D(D(r+1)**2))
 if r%1000==0:
 print(str((D(r)/D(upper))*100)[:5], '% ; Sum 2 of 2')
 twofac = D(2)**(-k)
 gammac = str(D(1)-(lnk*logsum)+logsum1+twofac)
 return D(gammac[:int(prec//6.66)])
#Calling Gamma
prec = int(input('Precision for Gamma: '))*8
decimal.getcontext().prec = prec
gam = gamma()
print(gam)
200_success
146k22 gold badges190 silver badges479 bronze badges
asked Sep 5, 2019 at 16:12
\$\endgroup\$
2
  • \$\begingroup\$ From purely mathematical point of view, you might be interested in this post, which has a collection of different algorithms for this constant \$\endgroup\$ Commented Sep 5, 2019 at 16:35
  • \$\begingroup\$ That is excellent, thank you! I need to learn to write latex. The top answer is indeed the algorithm I wrote here :) I believe the only way I can go faster, is to use the algorithm involving a(log(a)-1)=1 and binary splitting.(ignoring its python). \$\endgroup\$ Commented Sep 5, 2019 at 16:41

1 Answer 1

6
\$\begingroup\$
  • Computation of logsum and logsum1 in gamma() are suboptimal. You do costly operations of raising to power, and recompute factorial on each iteration (the latter invokes the quadratic time complexity BTW). Notice that in the \$\sum \dfrac{(-1)^{r-1} k^{r+1}}{(r+1)(r-1)!}\$ a consecutive term can be expressed via the previous one, as\$T_{r+1} = -k\dfrac{r+1}{(r+2)r} T_n\$. Instead of computing each term from scratch, use this recurrence, and enjoy a significant performance boost.

    Converting the summation into a Horner schema would likely improve the accuracy, that is to achieve the desired number of digits you'd need less number of terms.

  • Tree loops in lntwo cry to become a function.

answered Sep 5, 2019 at 16:40
\$\endgroup\$
1
  • \$\begingroup\$ Thank you! I will implement everything you have said. However I believe using the recurrence and/or the Horner schema will prevent me from multi-threading in the future, just as an FYI to readers. \$\endgroup\$ Commented Sep 5, 2019 at 17:04

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.