;ò
¿£ðEc           @   s  d  Z  d Z d k Z e i d e i ƒ Z d „  Z e i d „ Z e i e ƒ e i d „ Z	 e e i
 d „ Z d d	 d
 d d d g d e e i d „ Z e i
 e i e i d ƒ d „ Z d „  Z d e i
 d „ Z d e i d „ Z [ [ d „  Z d „  Z d „  Z d Z d S(   sb  Approximations to factorial (and its natural logarithm)

Stirling's formula gives an approximation to factorial:
  log(n!) = (n+.5) * log(n) - n + .5 * log(2*pi) -1/12/n 
with errors of order 1/n/n.

Lanczos's formula does pretty well too - see lngamma() below.  Note: the gamma
function attains a local minimum, 0.885603194411, at about 1.4616321 and takes
the value sqrt(pi)/2 = .886 at 3/2.

It is worth knowing that, once n! &larr; n is extended to the whole complex
plane, we find: (-n)!.n!.sinc(n.pi) = 1 for each complex non-integer n (and for
n=0).  Thus factorial has a simple pole at each negative integer.

The HAKMEM papers e.g. at
http://www.inwap.com/pdp10/hbaker/hakmem/hakmem.html
offer

  n! * (-n)! * sinc(pi*n) = 1
  (2*pi)**(.5*(n-1)) * (n*z)! / n**(n*z+.5) = z!*(z-1./n)!*...*(z-(n-1)/z)!

Stirling's formula gives an approximation to factorial:
  log(n!) = (n+.5) * log(n) - n + .5 * log(2*pi) -1/12/n 
with errors of order 1/n/n.  I have another scribble elsewhere which claims
  n! = n**(n+.5) * exp(-n) * (2*pi)**.5 * (1 +1/12/n +1/288/n/n +...)
which would appear to be disagreeing about the sign of the 1/12/n term.
It also suggests the tail will have a factor of 12 for each factor of n.

Lanczos's formula does pretty well too - see lngamma() below.  Note: the gamma
function attains a local minimum, 0.885603194411, at about 1.4616321 and takes
the value sqrt(pi)/2 = .886 at 3/2.

It is worth knowing that, once n! &larr; n is extended to the whole complex
plane, we find: (-n)!.n!.sinc(n.pi) = 1 for each complex non-integer n (and for
n=0).  Thus factorial has a simple pole at each negative integer.

For chose(N,m) = N!/m!/(N-m)! Stirling implies the approximation
  log(chose(n+m,m))
  = (n+m+.5)*log(n+m) -(n+.5)*log(n) -(m+.5)*log(m) -.5*log(2*pi) +(1/n +1/m -1/(n+m))/12
  = .5 * log((1/n +1/m)/2/pi) +n*log(1+m/n) +m*log(1+n/m) +(1+n/m+m/n)/(n+m)/12
s7   
$Id: stirling.py,v 1.4 2007/03/09 00:01:03 eddy Exp $
Ni   c         C   sw   d } xf |  d j oX y | |  |  d f \ } }  Wq	 t j
 o' t | ƒ |  |  d f \ } }  q	 Xq	 W| Sd  S(   Ni   (   s   results   ns   OverflowErrors   long(   s   ns   result(    (    s*   /home/eddy/.sys/py/study/maths/stirling.pys	   factorial2   s      *c         C   sC   d } x2 |  d j o$ | | |  ƒ |  d f \ } }  q	 W| Sd  S(   Nf0.0i   (   s   results   ns   log(   s   ns   logs   result(    (    s*   /home/eddy/.sys/py/study/maths/stirling.pys   lnfactorial<   s
      %c         C   s*   |  d | |  ƒ |  | d d |  Sd  S(   Nf0.5f1.0i   (   s   ns   logs   base(   s   ns   bases   log(    (    s*   /home/eddy/.sys/py/study/maths/stirling.pys
   lnStirlingC   s    c         C   s@   |  o  | | |  d d |  ƒ } n t |  d |  ƒ | Sd  S(   Nf1.0i   f0.5(   s   ns   __ss   exps   pow(   s   ns   __ss   exp(    (    s*   /home/eddy/.sys/py/study/maths/stirling.pys   StirlingF   s      f76.180091729471457f-86.505320329416776f24.014098240830911f-1.231739572450155f0.001208650973866179f5.3952393849530003e-06f1.0000000001900149c         C   sg   |  d } |  d | | ƒ | } x, | D]$ } | | |  d |  f \ } }  q) W| | | | ƒ Sd S(   sF  Lanczos's approximation to log(Gamma).

    This works by taking Stirling's formula, with Gamma(1+n) = n!, and putting
    in corrections for the first few poles in Gamma: for some whole g, N:

      Gamma(z+1) = pow(z +g +.5, z +.5)*exp(-(z +g +.5)) *sqrt(2*pi) *series
      with series = a + b/(z+1) +... +c/(z+N)
      for suitable constants a, b, ..., c

    Substitute x = z+1 to turn this into

      Gamma(x) = pow(x +g -.5, x -.5) *exp(-(x +g -.5)) *sqrt(2*pi) *series
      with series = a + sum(: coefficients[i]/(x+i) &larr; i :)
      for suitable a.

    Interestingly, Numerical Recipies (without explaining itself) uses Gamma(z)
    = Gamma(z+1)/z rather than substitution, as here; it also asserts that the
    error in the above requires a correction of less than 2e-10 in the series,
    for z with positive real part. f4.5f0.5i   N(   s   xs   bases   logs   coefficientss   cs   sums   scale(   s   xs   coefficientss   sums   scales   logs   cs   base(    (    s*   /home/eddy/.sys/py/study/maths/stirling.pys   lngammaL   s     
  "i   c         C   s  d } y |  i } Wn t j
 o |  } n XyA x: | d j  o, | d |  d |  d | f \ } }  } q5 WWn t j
 o t d ‚ n Xx6 | d j o( |  d | d f \ }  } | |  } q’ W|  d d f j o | Sn |  d j o | | Sn | | t |  ƒ ƒ Sd  S(   Ni   f1.0s9   The Gamma function has poles at all non-positive integersi   f1.5(	   s   results   xs   reals   rs   AttributeErrors   ZeroDivisionErrors   specials   exps   lngamma(   s   xs   exps   specials   rs   result(    (    s*   /home/eddy/.sys/py/study/maths/stirling.pys   gamman   s(        1   c         C   s5   y t  |  d ƒ SWn t j
 o t d ‚ n Xd  S(   Ni   s:   The factorial function has a pole at each negative integer(   s   gammas   xs   ZeroDivisionError(   s   x(    (    s*   /home/eddy/.sys/py/study/maths/stirling.pys	   gactorial   s     i   c         C   s¢   x4 | d j o& | |  d | | d f \ } } q Wx. | d j  o  | d } | | d |  } q: W| d j o | Sn | |  | | t d | ƒ ƒ Sd  S(   Ni   f1.0i    (   s   ns   scales   xs   exps   lngamma(   s   xs   ns   scales   exp(    (    s*   /home/eddy/.sys/py/study/maths/stirling.pys   expterm‡   s      ' 
 f1.0c         C   s   t  | | d |  d ƒ Sd  S(   Ni   f2.0(   s   expterms   pis   radiuss   dim(   s   dims   radiuss   pi(    (    s*   /home/eddy/.sys/py/study/maths/stirling.pys   sphere‘   s    c         C   s(   t  |  ƒ } t t |  ƒ | ƒ | Sd  S(   N(   s	   factorials   xs   fs   abss   Stirling(   s   xs   f(    (    s*   /home/eddy/.sys/py/study/maths/stirling.pys   error—   s    c         C   s(   t  |  ƒ } t t |  ƒ | ƒ | Sd  S(   N(   s   lnfactorials   xs   fs   abss
   lnStirling(   s   xs   f(    (    s*   /home/eddy/.sys/py/study/maths/stirling.pys   errorln›   s    c         C   s,   t  |  ƒ } t t d |  ƒ | ƒ | Sd  S(   Ni   (   s   lnfactorials   xs   fs   abss   lngamma(   s   xs   f(    (    s*   /home/eddy/.sys/py/study/maths/stirling.pys   gerrorŸ   s    sî  
$Log: stirling.py,v $
Revision 1.4  2007/03/09 00:01:03  eddy
Note contrary rumours.

Revision 1.3  2005/01/17 22:27:48  eddy
Added comment taken from HAKMEM note

Revision 1.2  2003/10/11 15:15:38  eddy
Made gamma cope better with complex (comparison has changed behaviour; it's
now not OK to ask whether greater/less than one), told it about special case
at 1.5 (since that happens to be analytically exact, and relevant to sphere).

Revision 1.1  2003/09/21 16:29:14  eddy
Initial revision
(   s   __doc__s   _rcs_id_s   maths   sqrts   pis	   roottwopis	   factorials   logs   lnfactorials
   lnStirlings   exps   Stirlings   lngammas   gammas	   gactorials   expterms   spheres   errors   errorlns   gerrors	   _rcs_log_(   s   lnfactorials   _rcs_id_s	   roottwopis   errorlns	   factorials   Stirlings
   lnStirlings   gerrors   expterms   spheres   gammas   errors	   _rcs_log_s   lngammas	   gactorials   math(    (    s*   /home/eddy/.sys/py/study/maths/stirling.pys   ?)   s"   		
+""	
			