mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-11-04 05:50:31 -05:00 
			
		
		
		
	
		
			
	
	
		
			162 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			162 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| 
								 | 
							
								[section:lgamma Log Gamma]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[h4 Synopsis]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								``
							 | 
						||
| 
								 | 
							
								#include <boost/math/special_functions/gamma.hpp>
							 | 
						||
| 
								 | 
							
								``
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   namespace boost{ namespace math{
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   template <class T>
							 | 
						||
| 
								 | 
							
								   ``__sf_result`` lgamma(T z);
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   template <class T, class ``__Policy``>
							 | 
						||
| 
								 | 
							
								   ``__sf_result`` lgamma(T z, const ``__Policy``&);
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   template <class T>
							 | 
						||
| 
								 | 
							
								   ``__sf_result`` lgamma(T z, int* sign);
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   template <class T, class ``__Policy``>
							 | 
						||
| 
								 | 
							
								   ``__sf_result`` lgamma(T z, int* sign, const ``__Policy``&);
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								   }} // namespaces
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[h4 Description]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The [@http://en.wikipedia.org/wiki/Gamma_function lgamma function] is defined by:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[equation lgamm1]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The second form of the function takes a pointer to an integer,
							 | 
						||
| 
								 | 
							
								which if non-null is set on output to the sign of tgamma(z).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[optional_policy]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[graph lgamma]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								There are effectively two versions of this function internally: a fully
							 | 
						||
| 
								 | 
							
								generic version that is slow, but reasonably accurate, and a much more
							 | 
						||
| 
								 | 
							
								efficient approximation that is used where the number of digits in the significand
							 | 
						||
| 
								 | 
							
								of T correspond to a certain __lanczos.  In practice, any built-in
							 | 
						||
| 
								 | 
							
								floating-point type you will encounter has an appropriate __lanczos
							 | 
						||
| 
								 | 
							
								defined for it.  It is also possible, given enough machine time, to generate
							 | 
						||
| 
								 | 
							
								further __lanczos's using the program libs/math/tools/lanczos_generator.cpp.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The return type of these functions is computed using the __arg_promotion_rules:
							 | 
						||
| 
								 | 
							
								the result is of type `double` if T is an integer type, or type T otherwise.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[h4 Accuracy]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The following table shows the peak errors (in units of epsilon) 
							 | 
						||
| 
								 | 
							
								found on various platforms
							 | 
						||
| 
								 | 
							
								with various floating point types, along with comparisons to 
							 | 
						||
| 
								 | 
							
								various other libraries. Unless otherwise specified any
							 | 
						||
| 
								 | 
							
								floating point type that is narrower than the one shown will have
							 | 
						||
| 
								 | 
							
								__zero_error.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Note that while the relative errors near the positive roots of lgamma
							 | 
						||
| 
								 | 
							
								are very low, the lgamma function has an infinite number of irrational
							 | 
						||
| 
								 | 
							
								roots for negative arguments: very close to these negative roots only
							 | 
						||
| 
								 | 
							
								a low absolute error can be guaranteed.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[table_lgamma]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[h4 Testing]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The main tests for this function involve comparisons against the logs of 
							 | 
						||
| 
								 | 
							
								the factorials which can be independently calculated to very high accuracy.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Random tests in key problem areas are also used.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[h4 Implementation]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The generic version of this function is implemented using Sterling's approximation
							 | 
						||
| 
								 | 
							
								for large arguments:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[equation gamma6]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								For small arguments, the logarithm of tgamma is used.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								For negative /z/ the logarithm version of the 
							 | 
						||
| 
								 | 
							
								reflection formula is used:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[equation lgamm3]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								For types of known precision, the __lanczos is used, a traits class 
							 | 
						||
| 
								 | 
							
								`boost::math::lanczos::lanczos_traits` maps type T to an appropriate
							 | 
						||
| 
								 | 
							
								approximation.  The logarithmic version of the __lanczos is:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[equation lgamm4]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Where L[sub e,g][space] is the Lanczos sum, scaled by e[super g].
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								As before the reflection formula is used for /z < 0/.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								When z is very near 1 or 2, then the logarithmic version of the __lanczos
							 | 
						||
| 
								 | 
							
								suffers very badly from cancellation error: indeed for values sufficiently
							 | 
						||
| 
								 | 
							
								close to 1 or 2, arbitrarily large relative errors can be obtained (even though
							 | 
						||
| 
								 | 
							
								the absolute error is tiny).  
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								For types with up to 113 bits of precision
							 | 
						||
| 
								 | 
							
								(up to and including 128-bit long doubles), root-preserving 
							 | 
						||
| 
								 | 
							
								rational approximations [jm_rationals] are used
							 | 
						||
| 
								 | 
							
								over the intervals [1,2] and [2,3].  Over the interval [2,3] the approximation
							 | 
						||
| 
								 | 
							
								form used is:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   lgamma(z) = (z-2)(z+1)(Y + R(z-2));
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								Where Y is a constant, and R(z-2) is the rational approximation: optimised
							 | 
						||
| 
								 | 
							
								so that it's absolute error is tiny compared to Y.  In addition 
							 | 
						||
| 
								 | 
							
								small values of z greater
							 | 
						||
| 
								 | 
							
								than 3 can handled by argument reduction using the recurrence relation:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   lgamma(z+1) = log(z) + lgamma(z);
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								Over the interval [1,2] two approximations have to be used, one for small z uses:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   lgamma(z) = (z-1)(z-2)(Y + R(z-1));
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								Once again Y is a constant, and R(z-1) is optimised for low absolute error
							 | 
						||
| 
								 | 
							
								compared to Y.  For z > 1.5 the above form wouldn't converge to a 
							 | 
						||
| 
								 | 
							
								minimax solution but this similar form does:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   lgamma(z) = (2-z)(1-z)(Y + R(2-z));
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								Finally for z < 1 the recurrence relation can be used to move to z > 1:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   lgamma(z) = lgamma(z+1) - log(z);
							 | 
						||
| 
								 | 
							
								   
							 | 
						||
| 
								 | 
							
								Note that while this involves a subtraction, it appears not
							 | 
						||
| 
								 | 
							
								to suffer from cancellation error: as z decreases from 1 
							 | 
						||
| 
								 | 
							
								the `-log(z)` term grows positive much more
							 | 
						||
| 
								 | 
							
								rapidly than the `lgamma(z+1)` term becomes negative.  So in this
							 | 
						||
| 
								 | 
							
								specific case, significant digits are preserved, rather than cancelled.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								For other types which do have a __lanczos defined for them 
							 | 
						||
| 
								 | 
							
								the current solution is as follows: imagine we
							 | 
						||
| 
								 | 
							
								balance the two terms in the __lanczos by dividing the power term by its value
							 | 
						||
| 
								 | 
							
								at /z = 1/, and then multiplying the Lanczos coefficients by the same value.
							 | 
						||
| 
								 | 
							
								Now each term will take the value 1 at /z = 1/ and we can rearrange the power terms
							 | 
						||
| 
								 | 
							
								in terms of log1p.  Likewise if we subtract 1 from the Lanczos sum part 
							 | 
						||
| 
								 | 
							
								(algebraically, by subtracting the value of each term at /z = 1/), we obtain
							 | 
						||
| 
								 | 
							
								a new summation that can be also be fed into log1p.  Crucially, all of the 
							 | 
						||
| 
								 | 
							
								terms tend to zero, as /z -> 1/:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[equation lgamm5]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The C[sub k][space] terms in the above are the same as in the __lanczos.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								A similar rearrangement can be performed at /z = 2/:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[equation lgamm6]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[endsect][/section:lgamma The Log Gamma Function]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[/ 
							 | 
						||
| 
								 | 
							
								  Copyright 2006 John Maddock and Paul A. Bristow.
							 | 
						||
| 
								 | 
							
								  Distributed under the Boost Software License, Version 1.0.
							 | 
						||
| 
								 | 
							
								  (See accompanying file LICENSE_1_0.txt or copy at
							 | 
						||
| 
								 | 
							
								  http://www.boost.org/LICENSE_1_0.txt).
							 | 
						||
| 
								 | 
							
								]
							 |