@@ -743,32 +743,100 @@ def gamma(x, prec)
743743 ( x + ( a - 1 ) ) . power ( x - 0.5 , prec2 ) . mult ( BigMath . exp ( 1 - x , prec2 ) , prec2 ) . mult ( sum , prec )
744744 end
745745
746+ # Calculates prod{x-k} and its coefficients for given ks, xn and prec with baby-step giant-step method.
747+ # xn is an array of precalculated powers of x: [1, x, x**2, x**3, ...]
748+ private_class_method def _x_minus_k_prod_coef ( ks , xn , prec )
749+ coef = [ 1 ]
750+ ks . each do |k |
751+ coef_next = [ 0 ] * ( coef . size + 1 )
752+ coef . each_with_index do |c , i |
753+ coef_next [ i ] -= k * c
754+ coef_next [ i + 1 ] += c
755+ end
756+ coef = coef_next
757+ end
758+
759+ prd = coef . each_with_index . map do |c , i |
760+ xn [ i ] . mult ( c , prec )
761+ end . reduce do |sum , value |
762+ sum . add ( value , prec )
763+ end
764+ [ prd , coef ]
765+ end
766+
746767 def gamma_lagrange ( x , prec )
768+ # Calculate approximage gamma by Lagrange interpolation of b**x/x! at x=b-l, b-l+1, ..., b+l
769+
747770 x = BigDecimal ( x ) - 1
748771 l = prec
749772 shift = x < 2 * prec ? 2 * prec - x . floor : 0
750773 x += shift
751- # Lagrange interpolate of b**x/x! at x=b-l, b-l+1, ..., b+l
752774 b = x . round
753- prods = ( ( b -l ) ..( b +l ) ) . map { |i | x - i } + shift . times . map { |i | x - i }
754- prods = prods . each_slice ( 2 ) . map { |a , b | b ? a . mult ( b , prec ) : a } while prods . size != 1
755- prod = prods . first
756775
757776 c0s = [ *( 1 ..b -l ) , *( 1 ..2 *l ) ]
758777 c0s = c0s . each_slice ( 2 ) . map { |a , b | b ? BigDecimal ( a ) . mult ( b , prec ) : BigDecimal ( a ) } while c0s . size != 1
759778 c0 = c0s . first
760779
780+ # Optimize this calculation for full-digit-x case and small-digit-x case
781+ # sum = BigDecimal(0)
782+ # prod = [*(b-l)..(b+l), *0...shift).map {|i| x - i }.reduce { _1.mult(_2, prec) }
783+ # c = BigDecimal(1)
784+ # ((b-l)..(b+l)).each do |i|
785+ # if i != b - l
786+ # c = c.mult(-b * (b + l - i + 1), prec).div((i - b + l) * i, prec)
787+ # end
788+ # sum = sum.add(c.div(x - i, prec), prec)
789+ # end
761790 if x . n_significant_digits > prec / 10
762- sum = BigDecimal ( 0 )
791+ # Reduce multiplications by baby-step giant-step method
792+
793+ batch_size = prec . bit_length
794+ # When expanding prod{x-k}, the coefficient of x**n might be huge.
795+ # Increase internal calculation precision to avoid loss of precision due to cancellation.
796+ internal_xn_prec = prec + ( Math . log10 ( b + l ) * batch_size ) . ceil
797+ xn = [ BigDecimal ( 1 ) ]
798+ xn << xn . last . mult ( x , internal_xn_prec ) while xn . size <= batch_size
799+
763800 c = BigDecimal ( 1 )
764- ( ( b -l ) ..( b +l ) ) . each do |i |
765- if i != b - l
766- c = c . mult ( -b * ( b + l - i + 1 ) , prec ) . div ( ( i - b + l ) * i , prec )
801+ sum = BigDecimal ( 0 )
802+ prod = BigDecimal ( 1 )
803+
804+ ( ( b -l ) ..( b +l ) ) . to_a . each_slice ( batch_size ) do |batch_ks |
805+ # Calculate prod{x-k} in this batch
806+ batch_prod , prod_coef = _x_minus_k_prod_coef ( batch_ks , xn , internal_xn_prec )
807+
808+ # Calculate coefficients of batch_prod/(x-k)
809+ batch_coef = [ 0 ] * batch_ks . size
810+ c_scale = 1 r
811+ batch_ks . each do |k |
812+ c_scale = c_scale * ( -b * ( b + l - k + 1 ) ) / ( ( k - b + l ) * k ) if k != b - l
813+ rem = 0
814+ ( batch_ks . size - 1 ) . downto ( 0 ) do |i |
815+ quo = prod_coef [ i + 1 ] + rem
816+ rem = quo * k
817+ batch_coef [ i ] += c_scale * quo
818+ end
767819 end
768- sum = sum . add ( c . div ( x - i , prec ) , prec )
820+
821+ batch_sum = BigDecimal ( 0 )
822+ batch_coef . each_with_index do |coef , i |
823+ batch_sum = batch_sum . add ( xn [ i ] . mult ( coef . numerator , internal_xn_prec ) . div ( coef . denominator , internal_xn_prec ) , internal_xn_prec )
824+ end
825+ sum = sum . add ( batch_sum . mult ( c , prec ) . div ( batch_prod , prec ) , prec )
826+ c = c . mult ( c_scale . numerator , prec ) . div ( c_scale . denominator , prec )
827+ prod = prod . mult ( batch_prod , prec )
828+ end
829+
830+ # Perform shift.times {|i| prod = prod.mult(x - i, prec) } with batch processing
831+ shift . times . to_a . each_slice ( batch_size ) do |batch_ks |
832+ shift_prod , _prod_coef = _x_minus_k_prod_coef ( batch_ks , xn , internal_xn_prec )
833+ prod = prod . mult ( shift_prod , prec )
769834 end
770835 else
771836 # Binary splitting
837+ prods = ( ( b -l ) ..( b +l ) ) . map { |i | x - i } + shift . times . map { |i | x - i }
838+ prods = prods . each_slice ( 2 ) . map { |a , b | b ? a . mult ( b , prec ) : a } while prods . size != 1
839+ prod = prods . first
772840 fractions = ( b - l + 1 ..b + l ) . map do |i |
773841 denominator = ( x - i ) . mult ( ( i - b + l ) * i , prec )
774842 numerator = ( x - i + 1 ) . mult ( -b * ( b + l - i + 1 ) , prec )
0 commit comments