Numerical algorithms III: special functions


Euler's Gamma function

Euler's Gamma function Gamma(z) is defined for complex z such that Re(z)>0 by the integral

Gamma(z):=Integrate(z,0,Infinity)Exp(-t)*t^(z-1).

The Gamma function satisfies several identities that can be proved by rearranging this integral; for example, Gamma(z+1)=z*Gamma(z). This identity defines Gamma(z) for all complex z. The Gamma function is regular everywhere except nonpositive integers (0, -1, -2, ...) where it diverges.

For real integers n>0, the Gamma function is the same as the factorial,

Gamma(n+1):=n!,

so the factorial notation can be used for the Gamma function too. Some formulae become a little simpler when written in factorials.

The Gamma function is implemented as Gamma(x). At integer values n of the argument, Gamma(n) is computed exactly. For half-integer arguments it is also computed exactly, using the following identities (here n is a nonnegative integer):

(+(2*n+1)/2)! =Sqrt(Pi)*(2*n+1)! /(2^(2*n+1)*n!),

(-(2*n+1)/2)! =(-1)^n*Sqrt(Pi)*(2^(2*n)*n!)/(2*n)!.

For efficiency, "double factorials" are used in this calculation.

For arbitrary complex arguments with nonnegative real part, the library function GammaNum(x) computes a uniform appoximation of Lanczos and Spouge (with the so-called "less precise coefficients of Spouge"). See: C. J. Lanczos, J. SIAM of Num. Anal. Ser. B, vol. 1, 86 (1964); J. L. Spouge, J. SIAM of Num. Anal., vol. 31, 931 (1994). See also: Paul Godfrey 2001 (unpublished): http://winnie.fit.edu/~gabdo/gamma.txt for some explanations on the method. The method gives the Gamma-function only for arguments with positive real part; at negative values of the real part of the argument, the Gamma-function is computed via the identity

Gamma(x)*Gamma(1-x)=Pi/Sin(Pi*x).

The Lanczos-Spouge approximation formula depends on a parameter a,

Gamma(z)=(Sqrt(2*Pi)*(z+a)^(z-1/2))/(z*e^(z+a))*(1+e^(a-1)/Sqrt(2*Pi)*Sum(k,1,N,c[k]/(z+k))),

with N:=Ceil(a)-1. The coefficients c[k] are defined by

c[k]=(-1)^(k-1)*(a-k)^(k-1/2)/(e^(k-1)*(k-1)!).

The parameter a is a free parameter of the approximation that determines also the number of terms in the sum. Some choices of a may lead to a slightly more precise approximation, but larger a is always better. The number of terms N must be large enough to produce the required precision. The estimate of the relative error for this formula is valid for all z such that Re(z)>0 and is

error<(2*Pi)^(-a)/Sqrt(2*Pi*a)*a/(a+z).

The lowest value of a to produce P correct digits is estimated as

a=(P-Ln(P)/Ln(10))*Ln(10)/Ln(2*Pi)-1/2.

In practical calculations, the integer logarithm routine IntLog is used and the constant Ln(10)/Ln(2*Pi) is approximated from above by 659/526, so that a is not underestimated.

The coefficients c[k] and the parameter a can be chosen to achieve a greater precision of the approximation formula. However, the recipe for the coefficients c[k] given in the paper by Lanczos is too complicated for practical calculations in arbitrary precision: the time it would take to compute the array of N coefficients c[k] grows as N^3. Therefore it is better to use less precise but much simpler formulae derived by Spouge.

In the calculation of the sum Sum(k,1,N,c[k]*(z+k)^(-1)), round-off error can lead to a serious loss of precision. At version 1.0.53rev1, Yacas is limited in its internal arbitrary precision facility that does not support true floating-point computation but rather uses fixed-point logic; this hinders precise calculations with floating-point numbers. (This concern does not apply to Yacas linked with gmp.) In the current version of the GammaNum() function, two workarounds are implemented. First, a Horner scheme is used to compute the sum; this is somewhat faster and leads to smaller roundoff errors. Second, intermediate calculations are performed at 40% higher precision than requested. This is much slower but allows to obtain results at desired precision.

If the factorial of a large integer or half-integer n needs to be computed not exactly but only with a certain floating-point precision, it is faster (for large enough Abs(n)) not to evaluate an exact integer product, but to use the GammaNum approximation or Stirling's asymptotic formula,

Ln(n!)<=>Ln(2*Pi*n)/2+n*Ln(n/e)+1/(12*n)-1/(360*n^3)+...

This method is currently not implemented in Yacas.


Other methods for the Gamma function

More traditional ways of calculating the Gamma function are the Stirling asymptotic series and the Sweeney-Brent method of combined series.

The Stirling asymptotic series is

Ln(Gamma(x))<>(x-1/2)*Ln(x)-x+1/2*Ln(2*Pi)

+Sum(n,1,Infinity,B[2*n]/(2*n*(2*n-1)*x^(2*n-1))).

This series requires finding the Bernoulli numbers B[n] up to a sufficiently high index n.

Using the identity x*Gamma(x)=Gamma(x+1), we can reduce the computation of Gamma(x) to Gamma(x+M) for some integer M. Then we can choose M to be large enough so that the Stirling asymptotic series gives the required precision when evaluated at x+M.

The second method for the Gamma function was used in Brent's Fortran MP package (R. P. Brent, A Fortran Multiple-Precision Arithmetic Package, ACM TOMS, Vol. 4, no. 1 (1978), p. 57). Brent refers to a 1963 paper by D. W. Sweeney for the origin of this method. Therefore we called this the "Sweeney-Brent" method.

The idea is to represent the Gamma function as a sum of two integrals,

Gamma(x)=Integrate(u,0,M)u^(x-1)*Exp(-u)+Integrate(u,M,Infinity)u^(x-1)*Exp(-u).

The above equation is clearly an identity for any M. Both integrals in this equation are approximated by power series. The parameter M can be chosen to minimize the error of the approximation, as we shall see below.

The first integral in this equation can be found as a sum of the Taylor series (expanding Exp(-u) near u=0),

Integrate(u,0,M)u^(x-1)*Exp(-u)=M^x*Sum(n,0,Infinity,((-1)^n*M^n)/((n+x)*n!)).

This series is an alternating series and its computation requires a significant increase of working precision, but the series absolutely converges for any finite M. The second integral can be approximated by an asymptotic series using standard methods of finding the asymptotics of integrals.

Calculations are faster if M is chosen as an integer value.


Euler's constant gamma

Euler's constant gamma is defined as

gamma:=Limit(n,Infinity)Sum(k,1,n,1/k)-Ln(n).

This constant is useful for various reasons, mostly when working with higher transcendental functions. For example, gamma is needed to obtain a Taylor series expansion of Gamma(x). Another useful relation is

D(x)Gamma(x)[x= -1]= -gamma.

Approximately gamma<=>0.577216.

Computing gamma by the series in the definition is extremely slow. A much faster method can be used, based on some identities of Bessel functions. (See R. P. Brent and E. M. McMillan, Some new algorithms for high precision computation of Euler's constant, Math. Comp. v. 34 (1980), 305; and also the online paper by X. Gourdon and P. Sebah, The Euler constant, online at http://numbers.computation.free.fr/Constants/ (2001).

The basic formulae for the "fast" method (Brent's method "B1") are:

gamma<=>S(n)/V(n)-Ln(n),

where S(n) and V(n) are some auxiliary functions, and n is chosen appropriately (basically, "high enough"; precise estimates are given).

First, the sequence H[n] is defined as the partial sum of the harmonic series:

H[n]:=Sum(k,1,n,1/k).

We also define H[0]:=0 for convenience. The function V(n) is the modified Bessel function I0(2*n). It is computed as a Taylor series

V(n):=Sum(k,0,Infinity,(n^k/k!)^2).

The function S(n) is defined by a series like V(n) but with each term multiplied by H[k]:

S(n):=Sum(k,0,Infinity,(n^k/k!)^2*H[k]).

Note that we need to compute S(n) and V(n) with enough precision, so the sum over k will have to be performed up to a large enough k. (In practice, we do not really need to know the limit k[max] beforehand. Instead, we can simply add terms to the series for V(n) and S(n) until desired precision is achieved. Knowing k[max] in advance would help only if we needed to compare this method for computing gamma with some other method, or if we would use the rectangular method of evaluating the Taylor series.)

According to Brent's paper, the error of this approximation of gamma, assuming that S(n) and V(n) are computed exactly, is

Abs(gamma-S(n)/V(n))<Pi*Exp(-4*n).

Therefore the parameter n is proportional to the number of digits we need. If we need P decimal digits (of absolute, not relative, precision), then we have to choose

n>(P*Ln(10)+Ln(Pi))/4.

The required number of terms k[max] in the summation over k to get S(n) and V(n) with this precision can be approximated as usual via Stirling's formula. It turns out that k[max] is also proportional to the number of digits, k[max]<=>2.07*P.

Therefore, this method of computing gamma has "linear convergence", i.e. the number of iterations is linear in the number of correct digits we need in the result. Of course, all calculations need to be performed with the working precision. The working precision must be a few digits more than P because we accumulate about Ln(k[max])/Ln(10) digits of roundoff error by performing k[max] arithmetic operations.

Brent mentions a small improvement on this method (his method "B3"). It consists of estimating the error of the approximation of gamma by an asymptotic series. Denote W(n) the function

W(n):=1/(4*n)*Sum(k,0,2*n,((2*k)!)^3/(k!)^4*(16*n)^(2*k)).

This function is basically the asymptotic series for I0(2*n)*K0(2*n), where I0 and K0 are modified Bessel functions. The sum in this series has to be evaluated until about k=2*n to get a good precision. Then a more precise approximation for gamma is

gamma<=>U(n)/V(n)-W(n)/V(n)^2.

The precision of this approximation is of order O(Exp(-8*n)) instead of O(Exp(-4*n)) in Brent's method "B1". However, this is not such a great savings in time, because almost as much additional work has to be done to compute W(n). Brent estimated this "B3" method to be about 20% faster than "B1".

Computation of S(n) seems to need H[k] first, and a long multiplication by H[k] at each term. To compute S(n) and V(n) faster and more accurately, Brent suggested the following trick that avoids this long multiplication and computes H[k] simultaneously with the series. Define the function U(n):=S(n)-Ln(n)*V(n). Then gamma<=>U(n)/V(n). The series for U(n) is U(n):=Sum(k,0,Infinity,A[k]), with

A[k]:=(n^k/k!)^2*(H[k]-Ln(n)).

If we denote

B[k]:=(n^k/k!)^2

the k-th term of the series for V(n), then we can compute A[k] and B[k] simultaneously using the recurrence relations A[0]= -Ln(n), B[0]=1,

B[k]=B[k-1]*n^2/k^2,

A[k]=1/k*(A[k-1]*n^2/k+B[k]).

All multiplications and divisions in these recurrence relations are performed with integers.

The time complexity of this method is O(P^2) where P is the required number of digits.

Also, it turns out that we can use a variant of the fast "rectangular method" to evaluate the series for U(n) and V(n) simultaneously. (We can consider these series as Taylor series in n^2.) This however does not speed up the evaluation of gamma. This happens because the rectangular method requires long multiplications and leads in this case to increased roundoff errors. The rectangular method for computing a power series in x is less efficient than a straightforward computation when x is a "short" rational or integer number.

Brent's "B1" method can be derived from the Taylor series for the modified Bessel function BesselI(nu,z),

BesselI(nu,z)=Sum(k,0,Infinity,z^(nu+2*k)/(2^(nu+2*k)*Gamma(nu+k+1)*k!)),

and the definition of the modified Bessel function K0(z),

K0(z):= -D(nu)BesselI(nu,z)[nu=0].

Here the derivative wrt nu is taken at nu=0. This derivative can be evaluated using the above Taylor series for BesselI(nu,z) and expressed through the series for S(n). To compute this, we need the derivative of the Gamma function at integer arguments n:

D(x)Gamma(x)[x=n+1]=n! *(H[n]-gamma).

The resulting identity in the way it is used here is

gamma+Ln(n)=(S0(2*n)-K0(2*n))/I0(2*n).

Since K0(2*n) decays very quickly at large n, we obtain the approximation

gamma<=>S0(2*n)/I0(2*n)-Ln(n)+O(Exp(-4*n)).


Riemann's Zeta function

Riemann's Zeta function zeta(s) is defined for complex s such that Re(s)>0 as a sum of inverse powers of integers:

zeta(s):=Sum(n,0,Infinity,1/n^s).

This function can be analytically continued to the entire complex plane except the point s=1 where it diverges. It satisfies several identities, for example, a formula useful for negative Re(s),

zeta(1-s)=(2*Gamma(s))/(2*Pi)^s*Cos((Pi*s)/2)*zeta(s),

and a formula for even integers that helps in numerical testing,

zeta(2*n)=((-1)^(n+1)*(2*Pi)^(2*n))/(2*(2*n)!)*B[2*n],

where B[n] are Bernoulli numbers.

The classic book of Bateman and Erdelyi, Higher Transcendental Functions, vol. 1, describes many results concerning analytic properties of zeta(s).

For the numerical evaluation of Riemann's Zeta function with arbitrary precision to become feasible, one needs special algorithms. Recently P. Borwein gave a simple and quick approximation algorithm for positive Re(s) (P. Borwein, An efficient algorithm for Riemann Zeta function (1995), published online and in Canadian Math. Soc. Conf. Proc., 27 (2000), 29-34.) See also: J. M. Borwein, D. M. Bradley, R. E. Crandall: Computation strategies for the Riemann Zeta function, preprint CECM-98-118, 1999, for a review of methods.

It is the "third" algorithm (the simplest one) from P. Borwein's paper which is implemented in Yacas. The approximation formula valid for Re(s)> -(n-1) is

zeta(s)=1/(2^n*(1-2^(1-s)))*Sum(j,0,2*n-1,e[j]/(j+1)^s),

where the coefficients e[j] for j=0, ..., 2*n-1 are defined by

e[j]:=(-1)^(j-1)*(Sum(k,0,j-n,n! /(k! *(n-k)!))-2^n),

and the empty sum (for j<n) is taken to be zero. The parameter n must be chosen high enough to achieve the desired precision. The error estimate for this formula is approximately

error<8^(-n)

for the relative precision, which means that to achieve P correct digits we must have n>P*Ln(10)/Ln(8).

The function Zeta(s) calls ZetaNum(s) to compute this approximation formula for Re(s)>1/2 and uses the identity above to get the value for other s.

For very large values of s, it is faster to use more direct methods implemented in the routines ZetaNum1(s,N) and ZetaNum2(s,N). If the required precision is P digits and s>1+Ln(10)/Ln(P)*P, then it is enough to compute the defining series for zeta(n),

zeta(n)<=>Sum(k,1,N,1/k^n),

up to a certain number of terms N. The required number of terms N is given by

N=10^(P/(s-1)).

This is implemented in the routine ZetaNum1(s). For example, at 100 digits of precision it is advisable to use ZetaNum1(s) only for s>50, since it would require N<110 terms in the series, whereas the expression used in ZetaNum(s) uses n=Ln(10)/Ln(8)*P terms (of a different series).

Alternatively, one can use ZetaNum2(n,N) which computes the infinite product over prime numbers p[i]

1/zeta(n)<=>Factorize(k,1,M,1-1/p[k]^s).

Here M must be chosen such that the M-th prime number p[M]>N. To obtain P digits of precision, N must be chosen as above, i.e. N>10^(P/(s-1)). Since only prime numbers p[i] are used, this formula is asymptotically faster than ZetaNum1. (Prime numbers have to be obtained and tested but this is quick for numbers of size n, compared with other operations.) The number of primes up to N is asymptotically pi(N)<>N/Ln(N) and therefore this procedure is faster by a factor O(Ln(N))<>O(Ln(n)). However, for n<250 it is better (with Yacas internal math) to use the ZetaNum1 routine because it involves fewer multiplications.


Lambert's W function

Lambert's W function is (a multiple-valued, complex function) defined for any (complex) z by W(z)*Exp(W(z))=z. This function is sometimes useful to represent solutions of transcendental equations or integrals.

Asymptotics of Lambert's W function are

W((z^2-2)/2/e)= -1+z-z^3/3+11/72*z^4-...

(this is only good really close to z=0); W(x)=x-x^2+3/2*x^3-... (this is good very near x=0);

W(x)=Ln(x)-Ln(Ln(x))+Ln(Ln(x))/Ln(x)+1/2*(Ln(Ln(x))/Ln(x))^2+...

(good for very large x but it is not straightforward to find higher terms!).

One can also find uniform rational approximations, e.g.:

W(x)<=>Ln(1+x)*(1-Ln(1+Ln(1+x))/(2+Ln(1+x)))

(uniformly good for x>0, relative error not more than 10^(-2)); and

W(x)<=>(x*e)/(1+((2*e*x+2)^(-1/2)-1/Sqrt(2)+1/(e-1))^(-1))

(uniformly good for -1/e<x<0, relative error not more than 10^(-3)).

There exists a uniform approximation of the form

W(x)<=>(a+L)*(f-Ln(1+c*Ln(1+d*Y))+2*L)/(a+1/2+L),

where a=2.3436, b=0.8842, c=0.9294, d=0.5106, f= -1.2133 are constants, Y:=Sqrt(2*e*x+2) and L:=Ln(1+b*Y). (The coefficients a, ..., f are found numerically by matching the asymptotics at three points x= -1/e, x=0, x=Infinity.) This approximation miraculously works over the whole complex plane within relative error at most 0.008. The behavior of this formula at the branching points x= -1/e and x=Infinity correctly mimics W(x) if the branch cuts of the square root and of the logarithm are chosen appropriately (e.g. the common branch cut along the negative real semiaxis).

The numerical procedure uses Halley's method. Halley's iteration for the equation W*Exp(W)=x can be written as

W'=W-(W-x*Exp(-W))/(W+1-(W+2)/(W+1)*(W-x*Exp(-W))/2).

It has cubic convergence for any initial value W> -Exp(-1).

The initial value is computed using one of the uniform approximation formulae. The good precision of the uniform approximation guarantees rapid convergence of the iteration scheme to the correct root of the equation, even for complex arguments x.


Bessel functions

Bessel functions are a family of special functions solving the equation

Deriv(x,2)w(x)+1/x*D(x)w(x)+(1-n^2/x^2)*w(x)=0.

There are two linearly independent solutions which can be taken as the pair of Hankel functions H1(n,x), H2(n,x), or as the pair of Bessel-Weber functions J[n], Y[n]. These pairs are linearly related, J[n]=1/2*(H1(n,x)+H2(n,x)), J[n]=1/(2*I)*(H1(n,x)-H2(n,x)). The function H2(n,x) is the complex conjugate of H1(n,x). This arrangement of four functions is very similar to the relation between Sin(x), Cos(x) and Exp(I*x), Exp(-I*x), which are all solutions of Deriv(x,2)f(x)+f(x)=0.

For large values of Abs(x), there is the following asymptotic series:

H1(n,x)<>Sqrt(2/(Pi*x))*Exp(I*zeta)*Sum(k,0,Infinity,I^k*A(k,n)/x^k),

where zeta:=x-1/2*n*Pi-1/4*Pi and

A(k,n):=((4*n^2-1^2)*(4*n^2-3^2)*...*(4*n^2-(2*k-1)^2))/(k! *8^k).

From this one can find the asymptotic series for J[n]<>Sqrt(2/(Pi*x))*Cos(zeta)*Sum(k,0,Infinity,(-1)^k*A(2*k,n)*x^(-2*k))-Sqrt(2/(Pi*x))*Sin(zeta)*Sum(k,0,Infinity,(-1)^k*A(2*k+1,n)*x^(-2*k-1)) and Y[n]<>Sqrt(2/(Pi*x))*Sin(zeta)*Sum(k,0,Infinity,(-1)^k*A(2*k,n)*x^(-2*k))+Sqrt(2/(Pi*x))*Cos(zeta)*Sum(k,0,Infinity,(-1)^k*A(2*k+1,n)*x^(-2*k-1)).

The error of a truncated asymptotic series is not larger than the first discarded term if the number of terms is larger than n-1/2. (See the book: F. W. J. Olver, Asymptotic and special functions, Academic Press, 1974.)

Currently Yacas can compute BesselJ(n,x) for all x where n is an integer and for Abs(x)<=2*Gamma(n) when n is a real number. Yacas currently uses the Taylor series when Abs(x)<=2*Gamma(n) to compute the numerical value:

BesselJ(n,x):=Sum(k,0,Infinity,(-1)^k*x^(2*k+n)/(2^(2*k+n)*k! *Gamma(k+n+1))).

If Abs(x)>2*Gamma(n) and n is an integer, then Yacas uses the forward recurrence relation:

BesselJ(n,x):=2*(n+1)/x*BesselJ(n+1,x)-BesselJ(n+2,x)

until the given BesselJ function is represented in terms of higher order terms which all satisfy Abs(x)<=2*Gamma(n). Note that when n is much smaller than x, this algorithm is quite slow because the number of Bessel function evaluations grows like 2^i, where i is the number of times the recurrence identity is used.

We see from the definition that when Abs(x)<=2*Gamma(n), the absolute value of each term is always decreasing (which is called absolutely monotonely decreasing.) From this we know that if we stop after i iterations, the error will be bounded by the absolute value of the next term. So given a set precision, turn this into a value epsilon, so that we can check if the current term will contribute to the sum at the prescribed precision. Before doing this, Yacas currently increases the precision by 20% to do interim calculations. This is a heuristic that works, it is not backed by theory. The value epsilon is given by epsilon:=5*10^(-prec), where prec was the previous precision. This is directly from the definition of floating point number which is correct to prec digits: A number correct to prec digits has a rounding error no greater than 5*10^(-prec). Beware that some books incorrectly have .5 instead of 5.

Bug: Something is not right with complex numbers, but pure imaginary are OK.


Bernoulli numbers and polynomials

The Bernoulli numbers B[n] come from a sequence of rational numbers defined by the series expansion of the following generating function,

z/(e^z-1)=Sum(n,0,Infinity,B[n]*z^n/n!).

The Bernoulli polynomials B(x)[n] are defined similarly by

(z*Exp(z*x))/(e^z-1)=Sum(n,0,Infinity,B(x)[n]*z^n/n!).

The Bernoulli polynomials are related to Bernoulli numbers by

B(x)[n]=Sum(k,0,n,Bin(n,k)*x^k*B[n-k]),

where Bin(n,k) are binomial coefficients.

Bernoulli numbers and polynomials are used in various Taylor series expansions, in the Euler-Maclauren series resummation formula, in Riemann's Zeta function and so on. For example, the sum of (integer) p-th powers of consecutive integers is given by

Sum(k,0,n-1,k^p)=(B(n)[p+1]-B[p+1])/(p+1).

The Bernoulli polynomials B(x)[n] can be found by first computing an array of Bernoulli numbers up to B[n] and then applying the above formula for the coefficients.

In this definition, the first Bernoulli numbers are B[0]=1, B[1]= -1/2, B[2]=4, B[3]=0, B[4]= -1/30.

We consider two distinct computational tasks: evaluate a Bernoulli number exactly as a rational, or find it approximately to a specified floating-point precision. There are also two possible problem settings: either we need to evaluate all Bernoulli numbers B[n] up to some n, or we only need one isolated value B[n] for some n. Depending on how large n is, different algorithms need to be used in these cases.


Exact evaluation of Bernoulli numbers

In the Bernoulli() routine, Bernoulli numbers are evaluated exactly (as rational numbers) via one of the two algorithms. The first, simpler algorithm (BernoullliArray()) uses the recurrence relation,

B[n]= -1/(n+1)*Sum(k,0,n-1,B[k]*Bin(n+1,k)).

This formula requires to know the entire set of B[k] with k up to a given n to compute B[n]. Therefore at large n this algorithm is a very slow way to compute B[n] if we do not need all other B[k].

Here is an estimate of the cost of BernoullliArray. Suppose M(P) is the time needed to multiply P-digit integers. The required number of digits P to store the numerator of B[n] is asymptotically P<>n*Ln(n). At each of the n iterations we need to multiply O(n) large rational numbers by large coefficients and take a GCD to simplify the resulting fractions. The time for GCD is logarithmic in P. So the complexity of this algorithm is O(n^2*M(P)*Ln(P)) with P<>n*Ln(n).

For large (even) values of the index n, the Bernoulli numbers B[n] are computed by a more efficient procedure: the integer part and the fractional part of B[n] are found separately.

First, by the theorem of Clausen -- von Staudt, the fractional part of (-B[n]) is the same as the fractional part of the sum of all inverse prime numbers p such that n is divisible by p-1. To illustrate the theorem, take n=10 with B[10]=5/66. The number n=10 is divisible only by 1, 2, 5, and 10; this corresponds to p=2, 3, 6 and 11. Of these, 6 is not a prime. Therefore, we exclude 6 and take the sum 1/2+1/3+1/11=61/66. The theorem now says that 61/66 has the same fractional part as -B[10]; in other words, -B[10]=i+f where i is some unknown integer and the fractional part f is a nonnegative rational number, 0<=f<1, which is now known to be 61/66. Indeed -B[10]= -1+61/66. So one can find the fractional part of the Bernoulli number relatively quickly by just checking the numbers that might divide n.

Now one needs to obtain the integer part of B[n]. The number B[n] is positive if Mod(n,4)=2 and negative if Mod(n,4)=0. One can use Riemann's Zeta function identity for even integer values of the argument and compute the value zeta(n) precisely enough so that the integer part of the Bernoulli number is determined. The required precision is found by estimating the Bernoulli number from the same identity in which one approximates zeta(n)=1, i.e.

Abs(B[2*n])<=>2*(2*n)! /(2*Pi)^(2*n).

To estimate the factorial of large numbers, we can use Stirling's asymptotic formula

Ln(n!)<=>Ln(2*Pi*n)/2+n*Ln(n/e).

The result is that for large n,

Abs(B[2*n])<>2*(n/(Pi*e))^(2*n).

At such large values of the argument n, it is feasible to use the routines ZetaNum1(n, N) or ZetaNum2(n,N) to compute the zeta function. These routines approximate zeta(n) by the defining series

zeta(n)<=>Sum(k,1,N,1/k^n).

The remainder of the sum is of order N^(-n). By straightforward algebra one obtains a lower bound on N,

N>n/(2*Pi*e),

for this sum to give enough precision to compute the integer part of the Bernoulli number B[n].

For example, let us compute B[20] using this method.

All these steps are implemented in the routine Bernoulli1. The variable Bernoulli1Threshold determines the smallest n for which B[n] is to be computed via this routine instead of the recursion relation. Its current value is 20.

The complexity of Bernoulli1 is estimated as the complexity of finding all primes up to n plus the complexity of computing the factorial, the power and the Zeta function. Finding the prime numbers up to n by checking all potential divisors up to Sqrt(n) requires O(n^(3/2)*M(Ln(n))) operations with precision O(Ln(n)) digits. For the second step we need to evaluate n!, Pi^n and zeta(n) with precision of P=O(n*Ln(n)) digits. The factorial is found in n short multiplications with P-digit numbers (giving O(n*P)), the power of pi in Ln(n) long multiplications (giving O(M(P)*Ln(n))), and ZetaNum2(n) (the asymptotically faster algorithm) requires O(n*M(P)) operations. The Zeta function calculation dominates the total cost because M(P) is slower than O(P). So the total complexity of Bernoulli1 is O(n*M(P)) with P<>n*Ln(n).

Note that this is the cost of finding just one Bernoulli number, as opposed to the O(n^2*M(P)*Ln(P)) cost of finding all Bernoulli numbers up to B[n] using the first algorithm BernoulliArray. If we need a complete table of Bernoulli numbers, then BernoulliArray is only marginally (logarithmically) slower. So for finding complete Bernoulli tables, Bernoulli1 is better only for very large n.


Approximate calculation of Bernoulli numbers

If Bernoulli numbers do not have to be found exactly but only to a certain floating-point precision P (this is usually the case for most numerical applications), then the situation is rather different. First, all calculations can be performed using floating-point numbers instead of exact rationals. This significantly speeds up the recurrence-based algorithms.

However, the recurrence relation used in BernoulliArray turns out to be numerically unstable and needs to be replaced by another (R. P. Brent, "A FORTRAN multiple-precision arithmetic package", ACM TOMS vol. 4, no. 1 (1978), p. 57). Brent's algorithm computes the Bernoulli numbers divided by factorials, C[n]:=B[2*n]/(2*n)! using a (numerically stable) recurrence relation

2*C[k]*(1-4^(-k))=(2*k-1)/(4^k*(2*k)!)-Sum(j,1,k-1,C[k-j]/(4^j*(2*j)!)).

The numerical instability of the usual recurrence relation

Sum(j,0,k-1,C[k-j]/(2*j+1)!)=(k-1/2)/(2*k+1)!

and the numerical stability of Brent's recurrence are not obvious. Here is one way to demonstrate them. Consider the usual recurrence (above). For large k, the number C[k] is approximately C[k]<=>2*(-1)^k*(2*Pi)^(-2*k). Suppose we use this recurrence to compute C[k] from previously found values C[k-1], C[k-2], etc. and suppose that we have small relative errors e[k] of finding C[k]. Then instead of the correct C[k] we use C[k]*(1+e[k]) in the recurrence. Now we can derive a relation for the error sequence e[k] using the approximate values of C[k]. It will be a linear recurrence of the form

Sum(j,0,k-1,(-1)^(k-j)*e[k-j]*(2*Pi)^(2*j)/(2*j+1)!)=(k-1/2)/(2*k+1)! *(2*Pi)^(-2*k).

Note that the coefficients for j>5 are very small but the coefficients for 0<=j<=5 are of order 1. This means that we have a cancellation in the first 5 or so terms that produces a very small number C[k] and this may lead to a loss of numerical precision. To investigate this loss, we find eigenvalues of the sequence e[k], i.e. we assume that e[k]=lambda^k and find lambda. If Abs(lambda)>1, then a small initial error e[1] will grow by a power of lambda on each iteration and it would indicate a numerical instability.

The eigenvalue of the sequence e[k] can be found approximately for large k if we notice that the recurrence relation for e[k] is similar to the truncated Taylor series for Sin(x). Substituting e[k]=lambda^k into it and disregarding a very small number (2*Pi)^(-2*k) on the right hand side, we find

Sum(j,0,k-1,(-lambda)^(k-j)*(2*Pi)^(2*j)/(2*j+1)!)<=>lambda^k*Sin((2*Pi)/Sqrt(lambda))<=>0,

which means that lambda=4 is a solution. Therefore the recurrence is unstable.

By a very similar calculation one finds that the inverse powers of 4 in Brent's recurrence make the largest eigenvalue of the error sequence e[k] almost equal to 1 and therefore the recurrence is stable. Brent gives the relative error in the computed C[k] as O(k^2) times the roundoff error in the last digit of precision.

The complexity of Brent's method is given as O(n^2*P+n*M(P)) for finding all Bernoulli numbers up to B[n] with precision P digits. This computation time can be achieved if we compute the inverse factorials and powers of 4 approximately by floating-point routines that know how much precision is needed for each term in the recurrence relation. The final long multiplication by (2*k)! computed to precision P adds M(P) to each Bernoulli number.

The non-iterative method using the Zeta function does not perform much better if a Bernoulli number B[n] has to be computed with significantly fewer digits P than the full O(n*Ln(n)) digits needed to represent the integer part of B[n]. (The fractional part of B[n] can always be computed relatively quickly.) The Zeta function needs 10^(P/n) terms, so its complexity is O(10^(P/n)*M(P)) (here by assumption P is not very large so 10^(P/n)<n/(2*Pi*e); if n>P we can disregard the power of 10 in the complexity formula). We should also add O(Ln(n)*M(P)) needed to compute the power of 2*Pi. The total complexity of Bernoulli1 is therefore O(Ln(n)*M(P)+10^(P/n)*M(P)).

If only one Bernoulli number is required, then Bernoulli1 is always faster. If all Bernoulli numbers up to a given n are required, then Brent's recurrence is faster for certain (small enough) n.

Currently Brent's recurrence is implemented as BernoulliArray1() but it is not used by Bernoulli because the internal arithmetic is not yet able to correctly compute with floating-point precision.


Error function Erf(x) and related functions

The error function Erf(z) is defined for any (complex) z by

Erf(z):=2/Sqrt(Pi)*Integrate(t,0,z)Exp(-t^2).

The complementary error function Erfc(x) is defined for real x as

Erfc(x):=2/Sqrt(Pi)*Integrate(t,x,Infinity)Exp(-t^2)=1-Erf(x).

The imaginary error function Erfi(x) is defined for real x as

Erfi(x):=2/Sqrt(Pi)*Integrate(t,x,Infinity)Exp(t^2).

Numerical computation of the error function Erf(z) needs to be performed by different methods depending on the value of z and its position in the complex plane, and on the required precision. We follow the book: Shulim E. Tsimring, Handbook of special functions and definite integrals: algorithms and programs for calculators, Radio and communications (publisher), Moscow, 1988 (in Russian), and the paper: Henry C. Thacher, Jr., Algorithm 180, Error function for large real X, Communications of the ACM, vol. 6, no. 6, 1963, p. 314. (These texts, however, do not describe arbitrary-precision computations.)

The function Erf(z) has the following approximations that are useful for its numerical computation:

Here we shall analyze the convergence and precision of these methods and show which one has to be chosen to compute Erf(z) with (relative) precision P decimal digits for a given (complex) number z, and obtain estimates for the necessary number of terms to take.

Both Taylor series converge absolutely for all z, but they do not converge uniformly fast; in fact these series are not very useful for large z because a very large number of slowly decreasing terms gives a significant contribution to the result, and the roundoff error (especially for the first series with the alternating signs) becomes too high. Both series converge well for Abs(z)<1.

Consider method 1 (the first Taylor series). We shall use the method 1 only for Abs(z)<=1. If the absolute error of the truncated Taylor series is estimated as the first discarded term, the precision after taking all terms up to and including z^(2*n) is approximately z^(2*n+2)/(n+2)!. The factorial can be approximated by Stirling's formula, n! <=>n^n*e^(-n). The value of Erf(z) at small z is of order 1, so we can take the absolute error to be equal to the relative error of the series that starts with 1. Therefore, to obtain P decimal digits of precision, we need the number of terms n that satisfies the inequality

Abs(e*z^2/(n+1))^(n+1)<10^(-P).

(We have substituted n+1 instead of n+2 which made the inequality stronger.) The error will be greatest when Abs(z)=1. For these values of z, the above inequality is satisfied when n>1+Exp(1+W(P*Ln(10)/e)) where W is Lambert's W function.

Consider method 3 (the asymptotic series). Due to limitations of the asymptotic series, we shall use the method 3 only for large enough values of z and small enough precision.

There are two important cases when calculating Erf(z) for large (complex) z: the case of z^2>0 and the case of z^2<0. In the first case (e.g. a real z), the function Erf(z) is approximately 1 for large Abs(z) (if Re(z)>0, and approximately -1 if Re(z)<0). In the second case (e.g. pure imaginary z=I*t) the function Erf(z) rapidly grows as Exp(-z^2)/z at large Abs(z).