bigdecimal 4.1.2-java → 4.1.3-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bigdecimal.gemspec +2 -0
- data/lib/bigdecimal/math/erf.rb +291 -0
- data/lib/bigdecimal/math/gamma.rb +513 -0
- data/lib/bigdecimal/math.rb +8 -219
- data/sig/big_decimal.rbs +3 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e2513e3ca1694eeafb719fc68e0b3fbddc8d358db7308e08d1877e4e50183eb5
|
|
4
|
+
data.tar.gz: 332693659e236e971b66264b8710690b2d80f9b8e517f70a8cf6843d471c5a0b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aa23abeaadf3797702ce3887b657871d669cf0cbd66d3c38cd7ca6fcbcd9b394ac2e6d8f1ddf887e619f3f627e78f05168dc9958d2860de5fc44edecd0d427c3
|
|
7
|
+
data.tar.gz: 8360c5dfa48f212fa033ad69f7eccfe5b321d8b9c7736980ce4140952d39f04b03069d441c5f59c77dec0250ed2e786b32c1a973712c4e2e9ff6f70a624bdae7
|
data/bigdecimal.gemspec
CHANGED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BigMath
|
|
4
|
+
# Bit-burst implementation of BigMath.erf and BigMath.erfc.
|
|
5
|
+
#
|
|
6
|
+
# Both functions share the same incremental update: given erf(x0+...+xk) (or erfc),
|
|
7
|
+
# extend to erf(x0+...+xk+x_{k+1}) by adding (or, for erfc, subtracting) the Taylor
|
|
8
|
+
# expansion of the difference function
|
|
9
|
+
# g(t) := (erf(t + a) - erf(a)) * exp(a**2) * sqrt(pi) / 2 with a = x0+...+xk
|
|
10
|
+
# which satisfies the homogeneous ODE g''(t) + 2*(t+a)*g'(t) = 0.
|
|
11
|
+
# Each step uses binary splitting on the 3-term recurrence of g's Taylor coefficients;
|
|
12
|
+
# split widths x1, x2, ... double in digits, giving quasi-linear total cost.
|
|
13
|
+
#
|
|
14
|
+
# Only the bit-burst seed differs between the two:
|
|
15
|
+
# erf : seed = erf(x0) via Taylor expansion at 0
|
|
16
|
+
# erfc : seed = erfc(x0) via asymptotic expansion (requires x0 large enough;
|
|
17
|
+
# returns nil if asymptotic cannot reach the requested precision, in which
|
|
18
|
+
# case erfc(x) is recovered from 1 - erf(x) with extra digits to absorb
|
|
19
|
+
# cancellation)
|
|
20
|
+
#
|
|
21
|
+
# Edge cases (after symmetry erf(-x) = -erf(x)):
|
|
22
|
+
# x == 0 : erf = 0
|
|
23
|
+
# x > 5e9 : erf = 1, erfc underflows
|
|
24
|
+
# x < 0.5 (erfc only) : compute via 1 - erf to avoid unnecessary work
|
|
25
|
+
module Erf # :nodoc:
|
|
26
|
+
|
|
27
|
+
# Calculates erf with given precision.
|
|
28
|
+
def self.erf(x, prec)
|
|
29
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :erf)
|
|
30
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erf)
|
|
31
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
32
|
+
return BigDecimal(x.infinite?) if x.infinite?
|
|
33
|
+
return BigDecimal(0) if x == 0
|
|
34
|
+
return -erf(-x, prec) if x < 0
|
|
35
|
+
return BigDecimal(1) if x > 5000000000 # erf(5000000000) > 1 - 1e-10000000000000000000
|
|
36
|
+
if x > 8
|
|
37
|
+
xf = x.to_f
|
|
38
|
+
log10_erfc = -xf ** 2 / Math.log(10) - Math.log10(xf * Math::PI ** 0.5)
|
|
39
|
+
erfc_prec = [prec + log10_erfc.ceil, 1].max
|
|
40
|
+
erfc = erfc_bit_burst(x, erfc_prec + BigDecimal::Internal::EXTRA_PREC)
|
|
41
|
+
return BigDecimal(1).sub(erfc, prec) if erfc
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
erf_bit_burst(x, prec + BigDecimal::Internal::EXTRA_PREC).mult(1, prec)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Calculates erfc with given precision.
|
|
48
|
+
def self.erfc(x, prec)
|
|
49
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :erfc)
|
|
50
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erfc)
|
|
51
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
52
|
+
return BigDecimal(1 - x.infinite?) if x.infinite?
|
|
53
|
+
return BigDecimal(1).sub(erf(x, prec + BigDecimal::Internal::EXTRA_PREC), prec) if x < 0.5
|
|
54
|
+
return BigDecimal::Internal.underflow_computation_result if x > 5000000000 # erfc(5000000000) < 1e-10000000000000000000 (underflow)
|
|
55
|
+
|
|
56
|
+
if x > 8
|
|
57
|
+
y = erfc_bit_burst(x, prec + BigDecimal::Internal::EXTRA_PREC)
|
|
58
|
+
return y.mult(1, prec) if y
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# erfc(x) = 1 - erf(x) < exp(-x**2)/x/sqrt(pi)
|
|
62
|
+
# Precision of erf(x) needs about log10(exp(-x**2)/x/sqrt(pi)) extra digits
|
|
63
|
+
log10 = 2.302585092994046
|
|
64
|
+
xf = x.to_f
|
|
65
|
+
high_prec = prec + BigDecimal::Internal::EXTRA_PREC + ((xf**2 + Math.log(xf) + Math.log(Math::PI)/2) / log10).ceil
|
|
66
|
+
BigDecimal(1).sub(erf_bit_burst(x, high_prec), prec)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Matrix multiplication. m1 and m2 are size*size length array that represents size*size matrix
|
|
70
|
+
def self.matrix_mult(m1, m2, size, prec)
|
|
71
|
+
(size * size).times.map do |i|
|
|
72
|
+
size.times.map do |k|
|
|
73
|
+
m1[i / size * size + k].mult(m2[size * k + i % size], prec)
|
|
74
|
+
end.reduce {|a, b| a.add(b, prec) }
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Returns (erf(x + a) - erf(a)) * exp(a**2) * sqrt(pi) / 2 calculated with binary splitting method.
|
|
79
|
+
def self.erf_binary_splitting_diff(x, a, prec)
|
|
80
|
+
# Let f(x) = (erf(x + a) - erf(a)) * exp(a**2) * sqrt(pi) / 2
|
|
81
|
+
# f(x) satisfies the following differential equation:
|
|
82
|
+
# 2*(x+a)*f'(x) + f''(x) = 0
|
|
83
|
+
# We can derive the following recurrence for the Taylor coefficients of f:
|
|
84
|
+
# f(x) = x * (c0 + c1*x + c2*x**2 + c3*x**3 + ...)
|
|
85
|
+
# c(0) = 1
|
|
86
|
+
# c(1) = -a
|
|
87
|
+
# c(i) = -2 * (a * c(i - 1) + c(i - 2) * (i - 1) / i) / (i + 1)
|
|
88
|
+
|
|
89
|
+
# Estimate required number of terms by calculating c(i) with low precision
|
|
90
|
+
low_prec = 10
|
|
91
|
+
a_low = a.mult(1, low_prec)
|
|
92
|
+
x_low = x.mult(1, low_prec)
|
|
93
|
+
coefs = [BigDecimal(1), -a_low]
|
|
94
|
+
xn = BigDecimal(1)
|
|
95
|
+
threshold = BigDecimal(1)._decimal_shift(-prec)
|
|
96
|
+
steps = (2..).find do |n|
|
|
97
|
+
prevprev, prev = coefs
|
|
98
|
+
xn = xn.mult(x_low, low_prec)
|
|
99
|
+
coefs = prev, (a_low * prev + (prevprev * (n - 1)).div(n, low_prec)).mult(-2, low_prec).div(n + 1, low_prec)
|
|
100
|
+
coefs[0].mult(xn, low_prec).abs < threshold && coefs[1].mult(xn * x_low, low_prec).abs < threshold
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Let M(i) be a 2x2 matrix that generates the next coefficients vector (c(i-1), c(i))
|
|
104
|
+
# from the previous two coefficients (c(i-2), c(i-1)).
|
|
105
|
+
# M(i) = | 0, 1 |
|
|
106
|
+
# | -2*(i-1)/i/(i+1), -2*a/(i+1) |
|
|
107
|
+
#
|
|
108
|
+
# Then, we can calculate (c(steps-1), c(steps)) as M(steps)*M(steps-1)*...*M(2)*Vector(c0, c1).
|
|
109
|
+
#
|
|
110
|
+
# Calculate a matrix that represents the sum of the Taylor series:
|
|
111
|
+
# SumMatrix = ((((...+I)x*M4+I)*x*M3+I)*M2*x+I)
|
|
112
|
+
# Actual sum can be calculated as:
|
|
113
|
+
# SumMatrix * Vector(c0, c1) = Vector(c0+c1*x+c2*x**2+c3*x**3+..., _)
|
|
114
|
+
# In this binary splitting method, adjacent two operations are combined into one repeatedly.
|
|
115
|
+
# ((...) * x * A + B) / C is the form of each operation. A and B are 2x2 matrices, C is a scalar.
|
|
116
|
+
|
|
117
|
+
zero = BigDecimal(0)
|
|
118
|
+
operations = (2..steps + 2).map do |i|
|
|
119
|
+
d = BigDecimal(i * (i + 1))
|
|
120
|
+
[[zero, d, BigDecimal(-2 * (i - 1)), a * (-2 * i)], [d, zero, zero, d], d]
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
while operations.size > 1
|
|
124
|
+
xpow = xpow ? xpow.mult(xpow, prec) : x.mult(1, prec)
|
|
125
|
+
operations = operations.each_slice(2).map do |op1, op2|
|
|
126
|
+
# Combine two operations into one:
|
|
127
|
+
# (((Remaining * x * A2 + B2) / C2) * x * A1 + B1) / C1
|
|
128
|
+
# ((Remaining * (x*x) * (A2*A1) + (x*B2*A1+B1*C2)) / (C1*C2)
|
|
129
|
+
# Therefore, combined operation can be represented as:
|
|
130
|
+
# Anext = A2 * A1
|
|
131
|
+
# Bnext = x * B2 * A1 + B1 * C2
|
|
132
|
+
# Cnext = C1 * C2
|
|
133
|
+
# xnext = x * x
|
|
134
|
+
a1, b1, c1 = op1
|
|
135
|
+
a2, b2, c2 = op2 || [[zero] * 4, [zero] * 4, BigDecimal(1)]
|
|
136
|
+
[
|
|
137
|
+
matrix_mult(a2, a1, 2, prec),
|
|
138
|
+
array_weighted_sum(matrix_mult(b2, a1, 2, prec), xpow, b1, c2, prec),
|
|
139
|
+
c1.mult(c2, prec),
|
|
140
|
+
]
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
_, sum_matrix, denominator = operations.first
|
|
144
|
+
sum = (sum_matrix[0] - a * sum_matrix[1]).div(denominator, prec)
|
|
145
|
+
x.mult(sum, prec)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Calculates erfc(x) using bit-burst algorithm.
|
|
149
|
+
# Returns nil if the asymptotic expansion does not reach the requested precision.
|
|
150
|
+
def self.erfc_bit_burst(x, prec)
|
|
151
|
+
# By bounding the relative error via |d(erfc)/erfc| <= 2*x*|dx| (erfc(x) decays as exp(-x**2)/x),
|
|
152
|
+
# truncate x to the minimum digits sufficient for prec-digit accuracy of the result.
|
|
153
|
+
x = x.mult(1, prec + Math.log10(2 * x.to_f**2).ceil)
|
|
154
|
+
erf_erfc_bit_burst(x, prec, start_digits: 40, mode: :erfc)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Calculates erf(x) using bit-burst algorithm.
|
|
158
|
+
def self.erf_bit_burst(x, prec)
|
|
159
|
+
# By bounding the error via erf'(x) = (2/sqrt(pi)) * exp(-x**2),
|
|
160
|
+
# truncate x to the minimum digits sufficient for prec-digit accuracy of the result.
|
|
161
|
+
x = x.mult(1, [(prec - x.floor**2 / Math.log(10) + Math.log10(x.ceil)).ceil, 10].max)
|
|
162
|
+
erf_erfc_bit_burst(x, prec, start_digits: 8, mode: :erf)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Calculates erf or erfc using bit-burst algorithm.
|
|
166
|
+
# Returns nil if erfc mode cannot reach the requested precision.
|
|
167
|
+
def self.erf_erfc_bit_burst(x, prec, start_digits:, mode:)
|
|
168
|
+
digits = [-x.exponent * 2, start_digits].max
|
|
169
|
+
partial = x.truncate(digits)
|
|
170
|
+
case mode
|
|
171
|
+
when :erf
|
|
172
|
+
f = erf_exp2_binary_splitting(partial, prec)
|
|
173
|
+
when :erfc
|
|
174
|
+
f = erfc_exp2_asymptotic_binary_splitting(partial, prec)
|
|
175
|
+
return unless f
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
exp_scale = BigMath.exp(-partial * partial, prec)
|
|
179
|
+
f = f.mult(exp_scale, prec)
|
|
180
|
+
|
|
181
|
+
calculated_x = partial
|
|
182
|
+
x -= partial
|
|
183
|
+
|
|
184
|
+
until x.zero?
|
|
185
|
+
digits *= 2
|
|
186
|
+
partial = x.truncate(digits)
|
|
187
|
+
next if partial.zero?
|
|
188
|
+
|
|
189
|
+
diff_prec = [prec - f.exponent + exp_scale.exponent + partial.exponent, 1].max
|
|
190
|
+
diff = erf_binary_splitting_diff(partial, calculated_x, diff_prec)
|
|
191
|
+
case mode
|
|
192
|
+
when :erf
|
|
193
|
+
f = f.add(diff.mult(exp_scale, prec), prec)
|
|
194
|
+
when :erfc
|
|
195
|
+
f = f.sub(diff.mult(exp_scale, prec), prec)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
calculated_x += partial
|
|
199
|
+
x -= partial
|
|
200
|
+
exp_scale = exp_scale.mult(BigMath.exp(partial * (partial - 2 * calculated_x), diff_prec), diff_prec) unless x.zero?
|
|
201
|
+
end
|
|
202
|
+
f.mult(BigDecimal(2).div(BigMath::PI(prec).sqrt(prec), prec), prec)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Matrix/Vector weighted sum
|
|
206
|
+
def self.array_weighted_sum(m1, w1, m2, w2, prec)
|
|
207
|
+
m1.zip(m2).map {|v1, v2| (v1 * w1).add(v2 * w2, prec) }
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Calculates Taylor expansion of erf(x)*exp(x**2)*sqrt(pi)/2 with binary splitting method.
|
|
211
|
+
def self.erf_exp2_binary_splitting(x, prec)
|
|
212
|
+
# Let f(x) = erf(x)*exp(x**2)*sqrt(pi)/2
|
|
213
|
+
# = c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...
|
|
214
|
+
# f(x) is designed to make all coefficients positive so that we don't need to consider cancellation error.
|
|
215
|
+
#
|
|
216
|
+
# f(x) satisfies the following differential equation:
|
|
217
|
+
# f'(x) = 1 + 2 * x * f(x)
|
|
218
|
+
# f'(x) = c1 + 2*c2*x + 3*c3*x**2 + 4*c4*x**3 + 5*c5*x**4 + ...
|
|
219
|
+
# = 1+2*x*(c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...)
|
|
220
|
+
# therefore,
|
|
221
|
+
# c0 = 0
|
|
222
|
+
# c1 = 1
|
|
223
|
+
# c2 = 2 * (c0 + c1) / 2
|
|
224
|
+
# c3 = 2 * (c1 + c2) / 3
|
|
225
|
+
# c4 = 2 * (c2 + c3) / 4
|
|
226
|
+
|
|
227
|
+
# Find the smallest n where the n-th Taylor term |c_n * x^n| falls below the precision
|
|
228
|
+
# threshold, using a Stirling-based upper bound on |c_n|.
|
|
229
|
+
log10f = Math.log(10)
|
|
230
|
+
cexponent = Math.log10(Math.sqrt(2)) + BigDecimal::Internal.float_log(x.abs) / log10f
|
|
231
|
+
|
|
232
|
+
x_to_f = x < 1e-300 ? 1e-300 : x.to_f # x.to_f may underflow when x is very small (e.g. 1e-400)
|
|
233
|
+
steps = (2..).bsearch do |n|
|
|
234
|
+
x_to_f ** 2 < n && n * cexponent + Math.lgamma(n / 2)[0] / log10f + n * Math.log10(2) - Math.lgamma(n - 1)[0] / log10f < -prec + x_to_f**2 / log10f
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
denominators = (steps / 2).times.map {|i| 2 * i + 3 }
|
|
238
|
+
x.mult(1 + BigDecimal::Internal.taylor_sum_binary_splitting(2 * x * x, denominators, prec), prec)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Calculates asymptotic expansion of erfc(x)*exp(x**2)*sqrt(pi)/2 with binary splitting method
|
|
242
|
+
def self.erfc_exp2_asymptotic_binary_splitting(x, prec)
|
|
243
|
+
# Let f(x) = erfc(x)*sqrt(pi)*exp(x**2)/2
|
|
244
|
+
# f(x) satisfies the following differential equation:
|
|
245
|
+
# 2*x*f(x) = f'(x) + 1
|
|
246
|
+
# From the above equation, we can derive the following asymptotic expansion:
|
|
247
|
+
# f(x) = (0..kmax).sum { (-1)**k * (2*k)! / 4**k / k! / x**(2*k) } / x / 2
|
|
248
|
+
|
|
249
|
+
# This asymptotic expansion does not converge.
|
|
250
|
+
# But if there is a k that satisfies (2*k)! / 4**k / k! / x**(2*k) < 10**(-prec),
|
|
251
|
+
# It is enough to calculate erfc within the given precision.
|
|
252
|
+
# Using Stirling's approximation, we can simplify this condition to:
|
|
253
|
+
# log(2)/2 + k*log(k) - k - 2*k*log(x) < -prec*log(10)
|
|
254
|
+
# and the left side is minimized when k = x**2.
|
|
255
|
+
xf = x.to_f
|
|
256
|
+
kmax = (1..(xf ** 2).floor).bsearch do |k|
|
|
257
|
+
Math.log(2) / 2 + k * Math.log(k) - k - 2 * k * Math.log(xf) < -prec * Math.log(10)
|
|
258
|
+
end
|
|
259
|
+
return unless kmax
|
|
260
|
+
|
|
261
|
+
# Convert asymptotic expansion to nested form:
|
|
262
|
+
# 1 + a/x + a*b/x/x + a*b*c/x/x/x + a*b*c/x/x/x*rest
|
|
263
|
+
# = 1 + (a/x) * (1 + (b/x) * (1 + (c/x) * (1 + rest)))
|
|
264
|
+
#
|
|
265
|
+
# And calculate it with binary splitting:
|
|
266
|
+
# (a1/d + b1/d * (a2/d + b2/d * (rest)))
|
|
267
|
+
# = ((a1*d+b1*a2)/(d*d) + b1*b2/(d*denominator) * (rest)))
|
|
268
|
+
denominator = x.mult(x, prec).mult(2, prec)
|
|
269
|
+
fractions = (1..kmax).map do |k|
|
|
270
|
+
[denominator, BigDecimal(1 - 2 * k)]
|
|
271
|
+
end
|
|
272
|
+
while fractions.size > 1
|
|
273
|
+
fractions = fractions.each_slice(2).map do |fraction1, fraction2|
|
|
274
|
+
a1, b1 = fraction1
|
|
275
|
+
a2, b2 = fraction2 || [BigDecimal(0), denominator]
|
|
276
|
+
[
|
|
277
|
+
a1.mult(denominator, prec).add(b1.mult(a2, prec), prec),
|
|
278
|
+
b1.mult(b2, prec),
|
|
279
|
+
]
|
|
280
|
+
end
|
|
281
|
+
denominator = denominator.mult(denominator, prec)
|
|
282
|
+
end
|
|
283
|
+
# Plug rest = 1 into the merged form: the innermost "(1 + rest)" of the nested expansion
|
|
284
|
+
# evaluates to 1 at truncation (rest = 0).
|
|
285
|
+
sum = fractions[0][0].add(fractions[0][1], prec).div(denominator, prec)
|
|
286
|
+
sum.div(x, prec) / 2
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
private_constant :Erf
|
|
291
|
+
end
|
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'bigdecimal/math'
|
|
3
|
+
|
|
4
|
+
module BigMath
|
|
5
|
+
|
|
6
|
+
# Calculates gamma/lgamma
|
|
7
|
+
# Algorithm overview:
|
|
8
|
+
#
|
|
9
|
+
# Lagrange interpolation of f(x) = b**x / x! at integer nodes x_i = b-l, ..., b+l.
|
|
10
|
+
# BSM(Binary Splitting Method) version for small digit numbers, O(PREC*log(PREC)^3)
|
|
11
|
+
# BSGS(Baby-Step Giant-Step) version for full digit numbers, O(PREC^2*log(log(PREC)))
|
|
12
|
+
# Both orders of magnitude faster than Spouge's approximation which is O(PREC^2*log(PREC))
|
|
13
|
+
# (Complexities assume quasi-linear multiplication, counting large-by-small products
|
|
14
|
+
# as (n/m) * M(m) = n * log(m) bit ops. BigDecimal multiplies the small coefficients
|
|
15
|
+
# by schoolbook instead: an extra log factor asymptotically, but faster at any feasible PREC.)
|
|
16
|
+
# Requires fast factorial of an integer near x (see Factorial Doubling below).
|
|
17
|
+
#
|
|
18
|
+
# Factorial Doubling for fast calculation of large factorials:
|
|
19
|
+
# Using Legendre duplication formula, we can calculate factorial(2n) from factorial(n) and factorial(n + 0.5).
|
|
20
|
+
# Calculating factorial(n + 0.5) is done by the BSM version of Lagrange interpolation in quasi-linear time.
|
|
21
|
+
# This will drastically reduce the cost of calculating large factorials.
|
|
22
|
+
# O(PREC*log(PREC)^3*log(factorial_argument))
|
|
23
|
+
#
|
|
24
|
+
# Stirling's approximation with Bernoulli numbers
|
|
25
|
+
# Only used when x is extremely large.
|
|
26
|
+
|
|
27
|
+
module Gamma # :nodoc:
|
|
28
|
+
|
|
29
|
+
# Calculates gamma function with given precision.
|
|
30
|
+
def self.gamma(x, prec)
|
|
31
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :gamma)
|
|
32
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :gamma)
|
|
33
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
34
|
+
|
|
35
|
+
if x < 0.5
|
|
36
|
+
raise Math::DomainError, 'Numerical argument is out of domain - gamma' if x.frac.zero?
|
|
37
|
+
|
|
38
|
+
# Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z)
|
|
39
|
+
pi = BigMath::PI(prec2)
|
|
40
|
+
sin = sinpix(x, pi, prec2)
|
|
41
|
+
pi.div(gamma(1 - x, prec2).mult(sin, prec2), prec)
|
|
42
|
+
else
|
|
43
|
+
# Digits of x beyond the working precision cannot affect the result.
|
|
44
|
+
# Rounding must happen before the integer test: an x indistinguishable from
|
|
45
|
+
# an integer must take the exact integer path, because gamma_lagrange
|
|
46
|
+
# requires a non-integer x (an integer x makes a node distance exactly zero).
|
|
47
|
+
x = x.mult(1, prec2 + x.exponent + 10)
|
|
48
|
+
if x.frac.zero?
|
|
49
|
+
integer_factorial(x.to_i - 1, prec2).mult(1, prec)
|
|
50
|
+
else
|
|
51
|
+
base, large_factorial_arg, small_factorial_arg, exp2 = gamma_lagrange(x, prec2)
|
|
52
|
+
ans = base.mult(integer_factorial(small_factorial_arg, prec2), prec2)
|
|
53
|
+
ans = ans.mult(BigDecimal(2).power(exp2, prec2), prec2) unless exp2.zero?
|
|
54
|
+
ans.mult(integer_factorial(large_factorial_arg, prec2), prec)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Calculates log gamma and its sign with given precision.
|
|
60
|
+
def self.lgamma(x, prec)
|
|
61
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :lgamma)
|
|
62
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :lgamma)
|
|
63
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
64
|
+
if x < 0.5
|
|
65
|
+
return [BigDecimal::INFINITY, 1] if x.frac.zero?
|
|
66
|
+
|
|
67
|
+
loop do
|
|
68
|
+
# Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z)
|
|
69
|
+
pi = BigMath::PI(prec2)
|
|
70
|
+
sin = sinpix(x, pi, prec2)
|
|
71
|
+
log_gamma = BigMath.log(pi, prec2).sub(lgamma(1 - x, prec2).first + BigMath.log(sin.abs, prec2), prec)
|
|
72
|
+
return [log_gamma, sin > 0 ? 1 : -1] if log_gamma != 0 && prec2 + log_gamma.exponent > prec + BigDecimal::Internal::EXTRA_PREC
|
|
73
|
+
|
|
74
|
+
# Retry with higher precision if loss of significance is too large
|
|
75
|
+
prec2 = prec2 * 3 / 2
|
|
76
|
+
end
|
|
77
|
+
else
|
|
78
|
+
# if x is close to 1 or 2, increase precision to reduce loss of significance
|
|
79
|
+
diff1_exponent = x < 3 ? (x - 1).exponent : 0
|
|
80
|
+
diff2_exponent = x < 3 ? (x - 2).exponent : 0
|
|
81
|
+
extremely_near_one = diff1_exponent < -prec2
|
|
82
|
+
extremely_near_two = diff2_exponent < -prec2
|
|
83
|
+
|
|
84
|
+
if extremely_near_one || extremely_near_two
|
|
85
|
+
# If x is extremely close to base = 1 or 2, linear interpolation is accurate enough.
|
|
86
|
+
# Taylor expansion at x = base is: (x - base) * digamma(base) + (x - base) ** 2 * trigamma(base) / 2 + ...
|
|
87
|
+
# And we can ignore (x - base) ** 2 and higher order terms.
|
|
88
|
+
base = extremely_near_one ? 1 : 2
|
|
89
|
+
d = BigDecimal(1)._decimal_shift(1 - prec2)
|
|
90
|
+
log_gamma_d, sign = lgamma(base + d, prec2)
|
|
91
|
+
return [log_gamma_d.mult(x - base, prec2).div(d, prec), sign]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
prec2 += [-diff1_exponent, -diff2_exponent, 0].max
|
|
95
|
+
|
|
96
|
+
# Same rounding as in Gamma.gamma. Must come after the near 1 and 2 handling
|
|
97
|
+
# above, which needs the exact distance from x to 1 or 2.
|
|
98
|
+
x = x.mult(1, prec2 + x.exponent + 10)
|
|
99
|
+
|
|
100
|
+
# When x is extremely large, the cost of Bernoulli number generation for Stirling's
|
|
101
|
+
# asymptotic expansion is smaller than the cost of multiple steps of doubling method.
|
|
102
|
+
# The condition is based on heuristic cost estimation and empirical tuning.
|
|
103
|
+
if x > prec2 && x.exponent > Integer.sqrt(prec2) / 6
|
|
104
|
+
[lgamma_stirling(x, prec2).mult(1, prec), 1]
|
|
105
|
+
elsif x.frac.zero?
|
|
106
|
+
[integer_factorial_log(x.to_i - 1, prec2).mult(1, prec), 1]
|
|
107
|
+
else
|
|
108
|
+
base, large_factorial_arg, small_factorial_arg, exp2 = gamma_lagrange(x, prec2)
|
|
109
|
+
lgamma = BigMath.log(base, prec2)
|
|
110
|
+
lgamma = lgamma.add(BigMath.log(2, prec2) * exp2, prec2) unless exp2.zero?
|
|
111
|
+
lgamma = lgamma.add(integer_factorial_log(small_factorial_arg, prec2), prec2)
|
|
112
|
+
lgamma = lgamma.add(integer_factorial_log(large_factorial_arg, prec2), prec)
|
|
113
|
+
[lgamma, 1]
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Calculates prod { x - k } for k in ks and the coefficients of the expanded polynomial.
|
|
119
|
+
# xn is an array of precalculated powers of x: [1, x, x**2, x**3, ...]
|
|
120
|
+
def self.x_minus_k_prod_coef(ks, xn, prec)
|
|
121
|
+
coef = [1]
|
|
122
|
+
ks.each do |k|
|
|
123
|
+
coef_next = [0] * (coef.size + 1)
|
|
124
|
+
coef.each_with_index do |c, i|
|
|
125
|
+
coef_next[i] -= k * c
|
|
126
|
+
coef_next[i + 1] += c
|
|
127
|
+
end
|
|
128
|
+
coef = coef_next
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
prod = coef.each_with_index.map do |c, i|
|
|
132
|
+
xn[i].mult(c, prec)
|
|
133
|
+
end.reduce do |sum, value|
|
|
134
|
+
sum.add(value, prec)
|
|
135
|
+
end
|
|
136
|
+
[prod, coef]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Bit length to keep in bit-dropping integer products and fraction merges.
|
|
140
|
+
# Only about prec * log2(10) bits are needed. 10 / 3 slightly exceeds log2(10),
|
|
141
|
+
# and 64 extra bits absorb the ~1 bit lost per truncation over the tree depth.
|
|
142
|
+
def self.drop_cap_bits(prec)
|
|
143
|
+
(prec * 10 + 192) / 3
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Calculate numbers.reduce(:*) of integers by Binary Splitting Method.
|
|
147
|
+
# Returns [mantissa, exp2] representing mantissa * 2**exp2.
|
|
148
|
+
# With cap, lower bits of intermediate products are dropped to keep each
|
|
149
|
+
# multiplication cost bounded by the target precision. Without cap, the product is exact.
|
|
150
|
+
def self.int_bsm_prod(numbers, cap = nil)
|
|
151
|
+
numbers = numbers.to_a
|
|
152
|
+
exp2 = 0
|
|
153
|
+
while numbers.size > 1
|
|
154
|
+
numbers = numbers.each_slice(2).map do |a, b|
|
|
155
|
+
next a unless b
|
|
156
|
+
v = a * b
|
|
157
|
+
if cap && (s = v.bit_length - cap) > 0
|
|
158
|
+
exp2 += s
|
|
159
|
+
v >>= s
|
|
160
|
+
end
|
|
161
|
+
v
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
[numbers.first || 1, exp2]
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Calculate factorial for integer n
|
|
168
|
+
def self.integer_factorial(n, prec)
|
|
169
|
+
power_part, exp2, exp_sqrtpi = integer_factorial_parameter(n, prec)
|
|
170
|
+
ans = BigDecimal(2).power(exp2, prec)
|
|
171
|
+
power_part.each_with_index do |base, index|
|
|
172
|
+
ans = ans.mult(base.power(1 << index, prec), prec)
|
|
173
|
+
end
|
|
174
|
+
if exp_sqrtpi != 0
|
|
175
|
+
pi = BigMath::PI(doubling_level_prec(prec, power_part.size - 1))
|
|
176
|
+
# exp_sqrtpi is 2**k - 1 (odd): the doubling recursion squares the child's
|
|
177
|
+
# sqrt(pi) exponent and adds one, so only the last level's sqrt survives
|
|
178
|
+
pipow = pi.power(exp_sqrtpi / 2, prec).mult(pi.sqrt(prec), prec)
|
|
179
|
+
ans = ans.div(pipow, prec)
|
|
180
|
+
end
|
|
181
|
+
ans
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Precision for values that integer_factorial raises to the power 2**index.
|
|
185
|
+
# The power multiplies their relative error by 2**index, so they need
|
|
186
|
+
# index * log10(2) more digits than the result.
|
|
187
|
+
def self.doubling_level_prec(prec, index)
|
|
188
|
+
prec + (index * Math.log10(2)).ceil + 1
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Calculate log factorial for integer n
|
|
192
|
+
def self.integer_factorial_log(n, prec)
|
|
193
|
+
power_part, exp2, exp_sqrtpi = integer_factorial_parameter(n, prec)
|
|
194
|
+
ans = exp2.zero? ? BigDecimal(0) : BigMath.log(2, prec) * exp2
|
|
195
|
+
power_part.each_with_index do |base, index|
|
|
196
|
+
ans = ans.add(BigMath.log(base, prec) * (1 << index), prec)
|
|
197
|
+
end
|
|
198
|
+
if exp_sqrtpi != 0
|
|
199
|
+
pi = BigMath::PI(prec)
|
|
200
|
+
ans = ans.sub(BigMath.log(pi, prec) * (BigDecimal(exp_sqrtpi) / 2), prec)
|
|
201
|
+
end
|
|
202
|
+
ans
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Calculates parameters for integer factorial calculation.
|
|
206
|
+
# Returns [base_power_part, exp2, exp_sqrtpi] that can produce factorial(n) as:
|
|
207
|
+
# factorial(n) = prod { base_power_part[i]**(1 << i) } * 2**exp2 / sqrt(pi)**exp_sqrtpi
|
|
208
|
+
# These parameters are used to avoid overflow when calculating log factorial and lgamma for large n.
|
|
209
|
+
def self.integer_factorial_parameter(n, prec)
|
|
210
|
+
base_power_part, factorial_power_part, exp2, exp_sqrtpi = integer_factorial_recursive(n, prec)
|
|
211
|
+
fact_x = 1
|
|
212
|
+
fact_y = BigDecimal(1)
|
|
213
|
+
# factorial_power_part is non-decreasing (deeper recursion levels have smaller b,
|
|
214
|
+
# and gamma_lagrange_l grows as b shrinks), so fact_y can be extended incrementally.
|
|
215
|
+
# fact_y is carried over to every later index, so it needs the precision
|
|
216
|
+
# of the deepest level from the start.
|
|
217
|
+
level_prec = doubling_level_prec(prec, factorial_power_part.size - 1)
|
|
218
|
+
factorial_power_part.each_with_index do |factorial_arg, index|
|
|
219
|
+
# Exact product (no bit drop): these ranges total only O(prec * log(prec)) digits,
|
|
220
|
+
# and a dropped 2**s here would be raised to 2**index, exceeding the representable
|
|
221
|
+
# exponent range while base_power_part[index] underflows by the same amount.
|
|
222
|
+
mantissa, = int_bsm_prod(fact_x + 1..factorial_arg)
|
|
223
|
+
fact_y = fact_y.mult(mantissa, level_prec)
|
|
224
|
+
fact_x = factorial_arg
|
|
225
|
+
base_power_part[index] = base_power_part[index].mult(fact_y, level_prec)
|
|
226
|
+
end
|
|
227
|
+
[base_power_part, exp2, exp_sqrtpi]
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Returns [base_power_part, factorial_power_part, exp2, exp_sqrtpi] that can produce factorial(n) as:
|
|
231
|
+
# factorial(n) = prod { base_power_part[i]**(1 << i) } * prod { factorial(factorial_power_part[i])**(1 << i) } * 2**exp2 / sqrt(pi)**(exp_sqrtpi)
|
|
232
|
+
# If n is large, this method recursively calculates factorial for smaller n by Legendre duplication formula.
|
|
233
|
+
# index is the recursion depth, which is also the position in the returned arrays.
|
|
234
|
+
def self.integer_factorial_recursive(n, prec, index = 0)
|
|
235
|
+
level_prec = doubling_level_prec(prec, index)
|
|
236
|
+
if n < 4 * prec
|
|
237
|
+
mantissa, exp2 = int_bsm_prod(1..n, drop_cap_bits(level_prec))
|
|
238
|
+
return [[BigDecimal(mantissa)], [], exp2, 0]
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Use Legendre duplication formula to reduce factorial(n) to half-size factorials:
|
|
242
|
+
# factorial(n) = factorial(n/2.0) * factorial((n-1)/2.0) * 2**n / sqrt(pi)
|
|
243
|
+
# gamma_lagrange((n + 1) / 2 + 0.5, prec) computes the half-integer factorial
|
|
244
|
+
# (whichever of the two factors above is a half-integer).
|
|
245
|
+
half_arg = BigDecimal((n + 1) / 2) + BigDecimal('0.5')
|
|
246
|
+
base, large_factorial_arg, small_factorial_arg, lagrange_exp2 = gamma_lagrange(half_arg, level_prec)
|
|
247
|
+
|
|
248
|
+
range_mantissa, = int_bsm_prod(large_factorial_arg + 1..n / 2) # exact: total size is O(prec) digits
|
|
249
|
+
base = base.mult(range_mantissa, level_prec)
|
|
250
|
+
base_power_part, factorial_power_part, exp2, exp_sqrtpi = integer_factorial_recursive(large_factorial_arg, prec, index + 1)
|
|
251
|
+
[
|
|
252
|
+
[base] + base_power_part,
|
|
253
|
+
[small_factorial_arg] + factorial_power_part,
|
|
254
|
+
exp2 * 2 + n + lagrange_exp2,
|
|
255
|
+
exp_sqrtpi * 2 + 1
|
|
256
|
+
]
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Estimate the required number of interpolation points `l` to achieve `prec` digits.
|
|
260
|
+
#
|
|
261
|
+
# Assuming the nodes stay strictly positive, the function b^x/x! approximates a
|
|
262
|
+
# Gaussian curve e^(-y^2 / 2b) around its peak (x=b).
|
|
263
|
+
# The Taylor coefficient of degree 2l is roughly c_2l = 1 / (l! * (2b)^l).
|
|
264
|
+
# Multiplying this by the distance product of 2l+1 nodes (approx (l/e)^(2l)),
|
|
265
|
+
# the overall truncation error E is bounded by: E ~ (l / 2eb)^l.
|
|
266
|
+
#
|
|
267
|
+
# Setting E <= 10^-prec gives the implicit equation:
|
|
268
|
+
# l * log10(2 * e * b / l) = prec => l = prec / log10(2 * e * b / l)
|
|
269
|
+
def self.gamma_lagrange_l(b, prec)
|
|
270
|
+
# Initial guess of l. When b >= 2 * prec - 1 (guaranteed by the shift in gamma_lagrange),
|
|
271
|
+
# this is safely larger than the actual l.
|
|
272
|
+
l = prec
|
|
273
|
+
|
|
274
|
+
# Solves the implicit equation via fixed-point iteration.
|
|
275
|
+
# Due to the slow growth of the logarithm, 2 iterations are practically sufficient.
|
|
276
|
+
2.times { l = prec / Math.log10(2 * Math::E * b / l) }
|
|
277
|
+
l.ceil + 10 # Adds safety margin
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Calculate approximate gamma by Lagrange interpolation of f(x) = b**x / x!
|
|
281
|
+
# Nodes are placed at x_i = b-l, b-l+1, ..., b+l.
|
|
282
|
+
# b: x.round, l: number of nodes on one side (total nodes = 2*l+1)
|
|
283
|
+
#
|
|
284
|
+
# Mathematically, we use the barycentric interpolation form:
|
|
285
|
+
# f(x) \approx \omega(x) \sum_{i} \frac{w_i f(x_i)}{x - x_i}
|
|
286
|
+
# Therefore, \Gamma(x+1) = x! = b**x / f(x)
|
|
287
|
+
#
|
|
288
|
+
# Time complexity:
|
|
289
|
+
# - O(PREC*log(PREC)^3) for small-digit x (Binary Splitting)
|
|
290
|
+
# - O(PREC^2*log(log(PREC))) for full-digit x (Baby-step Giant-step).
|
|
291
|
+
# Measured time grows like PREC^2.
|
|
292
|
+
#
|
|
293
|
+
# Returns [base, large_factorial_arg, small_factorial_arg, exp2] that can produce gamma(x) as:
|
|
294
|
+
# gamma(x) = base * 2**exp2 * factorial(large_factorial_arg) * factorial(small_factorial_arg)
|
|
295
|
+
def self.gamma_lagrange(x, prec)
|
|
296
|
+
# Shift x to establish a safe center (b) for the barycentric interpolation.
|
|
297
|
+
#
|
|
298
|
+
# We must keep all interpolation nodes strictly positive (b - l > 0). Approaching
|
|
299
|
+
# x = 0 breaks the Gaussian approximation used to estimate `l` and provides no
|
|
300
|
+
# useful information for the interpolation.
|
|
301
|
+
#
|
|
302
|
+
# While b =~ 1.36 * prec is the strict theoretical minimum to stay positive, we
|
|
303
|
+
# heuristically use b = 2 * prec. This moves the nodes safely away from x = 0,
|
|
304
|
+
# stabilizes the curve, and empirically yields the optimal total computation cost.
|
|
305
|
+
# (See `gamma_lagrange_l` for the mathematical derivation of the approximation).
|
|
306
|
+
shift = x < 2 * prec ? 2 * prec - x.floor : 0
|
|
307
|
+
x += shift
|
|
308
|
+
|
|
309
|
+
x = BigDecimal(x) - 1
|
|
310
|
+
b = x.round
|
|
311
|
+
l = gamma_lagrange_l(b, prec)
|
|
312
|
+
exp2 = 0
|
|
313
|
+
|
|
314
|
+
# --- Reference: Naive interpolation logic ---
|
|
315
|
+
# The two branches below optimize this calculation for the full-digit and small-digit x cases.
|
|
316
|
+
# sum = BigDecimal(0)
|
|
317
|
+
# prod = [*(b - l..b + l), *(0...shift)].map {|i| x - i }.reduce { _1.mult(_2, prec) }
|
|
318
|
+
# c = BigDecimal(1) # represents w_i * f(x_i) (normalized)
|
|
319
|
+
# (b - l..b + l).each do |i|
|
|
320
|
+
# if i != b - l
|
|
321
|
+
# c = c.mult(-b * (b + l - i + 1), prec).div((i - b + l) * i, prec)
|
|
322
|
+
# end
|
|
323
|
+
# sum = sum.add(c.div(x - i, prec), prec)
|
|
324
|
+
# end
|
|
325
|
+
# --------------------------------------------
|
|
326
|
+
|
|
327
|
+
# Choose between BSM and BSGS based on total bit cost:
|
|
328
|
+
# BSM: (l * n_sig / prec) full-digit multiplications, each costing prec * log(prec)
|
|
329
|
+
# bit ops, total l * n_sig * log(prec).
|
|
330
|
+
# BSGS: l * prec bit ops with batch_size = log2(prec) (see below).
|
|
331
|
+
# Cross-over: n_sig * log(prec) > prec.
|
|
332
|
+
if x.n_significant_digits * prec.bit_length > prec
|
|
333
|
+
# Reduce full-precision multiplications/divisions using a Batched Evaluation
|
|
334
|
+
# inspired by the Baby-Step Giant-Step (BSGS) method.
|
|
335
|
+
|
|
336
|
+
# Normal BSGS uses batch_size = sqrt(l), but here the integer coefficients of the
|
|
337
|
+
# expanded prod { x - k } over a batch grow like (b + l)**batch_size, so a smaller
|
|
338
|
+
# batch keeps both the coefficient size and the per-batch evaluation cost low.
|
|
339
|
+
# batch_size = log2(prec) brings the total BSGS bit cost down to O(l * prec * log(log(prec))).
|
|
340
|
+
batch_size = prec.bit_length
|
|
341
|
+
|
|
342
|
+
# When expanding prod { x - k }, the coefficient of x**n might be huge.
|
|
343
|
+
# Increase internal calculation precision to avoid catastrophic cancellation.
|
|
344
|
+
# When x is within 10**-q of a node, batch_prod cancels by q more digits;
|
|
345
|
+
# without the extra digits the computed batch_prod can even collapse to
|
|
346
|
+
# exactly zero, and the division below would raise or produce NaN.
|
|
347
|
+
nearest_node_distance = x - x.round(0, BigDecimal::ROUND_HALF_UP)
|
|
348
|
+
near_node_digits = [1 - nearest_node_distance.exponent, 0].max
|
|
349
|
+
internal_xn_prec = prec + (Math.log10(b + l) * batch_size).ceil + near_node_digits
|
|
350
|
+
xn = [BigDecimal(1)]
|
|
351
|
+
xn << xn.last.mult(x, internal_xn_prec) while xn.size <= batch_size
|
|
352
|
+
|
|
353
|
+
c = BigDecimal(1)
|
|
354
|
+
sum = BigDecimal(0)
|
|
355
|
+
prod = BigDecimal(1)
|
|
356
|
+
|
|
357
|
+
# sum, c and prod are updated once per batch, so their rounding errors
|
|
358
|
+
# accumulate in proportion to the number of batches.
|
|
359
|
+
batch_count = (2 * l + 1 + shift) / batch_size + 1
|
|
360
|
+
accumulate_prec = prec + Math.log10(batch_count).ceil + 1
|
|
361
|
+
|
|
362
|
+
((b - l)..(b + l)).to_a.each_slice(batch_size) do |batch_ks|
|
|
363
|
+
# Calculate prod{ x - k } in this batch
|
|
364
|
+
batch_prod, prod_coef = x_minus_k_prod_coef(batch_ks, xn, internal_xn_prec)
|
|
365
|
+
|
|
366
|
+
# Calculate coefficients of batch_prod / (x - k) using Synthetic Division (Ruffini's rule)
|
|
367
|
+
batch_coef = [0] * batch_ks.size
|
|
368
|
+
c_scale = 1r
|
|
369
|
+
batch_ks.each do |k|
|
|
370
|
+
c_scale = c_scale * (-b * (b + l - k + 1)) / ((k - b + l) * k) if k != b - l
|
|
371
|
+
rem = 0
|
|
372
|
+
(batch_ks.size - 1).downto(0) do |i|
|
|
373
|
+
quo = prod_coef[i + 1] + rem
|
|
374
|
+
rem = quo * k
|
|
375
|
+
batch_coef[i] += c_scale * quo
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
batch_sum = BigDecimal(0)
|
|
380
|
+
batch_coef.each_with_index do |coef, i|
|
|
381
|
+
batch_sum = batch_sum.add(xn[i].mult(coef.numerator, internal_xn_prec).div(coef.denominator, internal_xn_prec), internal_xn_prec)
|
|
382
|
+
end
|
|
383
|
+
# batch_prod loses relative accuracy when x is extremely close to a node in this
|
|
384
|
+
# batch. This is harmless: the same computed value is divided into sum here and
|
|
385
|
+
# multiplied into prod below, so the error cancels in the final prod * sum.
|
|
386
|
+
sum = sum.add(batch_sum.mult(c, accumulate_prec).div(batch_prod, accumulate_prec), accumulate_prec)
|
|
387
|
+
c = c.mult(c_scale.numerator, accumulate_prec).div(c_scale.denominator, accumulate_prec)
|
|
388
|
+
prod = prod.mult(batch_prod, accumulate_prec)
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# Perform shift.times {|i| prod = prod.mult(x - i, prec) } with batch processing
|
|
392
|
+
shift.times.to_a.each_slice(batch_size) do |batch_ks|
|
|
393
|
+
shift_prod, _prod_coef = x_minus_k_prod_coef(batch_ks, xn, internal_xn_prec)
|
|
394
|
+
prod = prod.mult(shift_prod, accumulate_prec)
|
|
395
|
+
end
|
|
396
|
+
else
|
|
397
|
+
# Binary Splitting Method (BSM) for short-digit inputs.
|
|
398
|
+
# Scaling x by 10**frac_digits makes every node term x - i an exact integer,
|
|
399
|
+
# so the whole tree runs on Integer arithmetic. The scale cancels inside the
|
|
400
|
+
# fraction merges; only prod and sum need explicit rescaling.
|
|
401
|
+
frac_digits = [x.n_significant_digits - x.exponent, 0].max
|
|
402
|
+
s10 = 10**frac_digits
|
|
403
|
+
xs = x._decimal_shift(frac_digits).to_i
|
|
404
|
+
cap = drop_cap_bits(prec)
|
|
405
|
+
|
|
406
|
+
prod_factors = (b - l..b + l).map {|i| xs - i * s10 } + shift.times.map {|i| xs - i * s10 }
|
|
407
|
+
mantissa, dropped_exp2 = int_bsm_prod(prod_factors, cap)
|
|
408
|
+
prod = BigDecimal(mantissa)._decimal_shift(-frac_digits * prod_factors.size)
|
|
409
|
+
# prod is missing the dropped 2**dropped_exp2 factor and gamma is proportional
|
|
410
|
+
# to 1 / prod. Returning the compensation as exp2 lets the factorial doubling
|
|
411
|
+
# fold it into its own 2**exp2 channel instead of paying a power here.
|
|
412
|
+
exp2 = -dropped_exp2
|
|
413
|
+
|
|
414
|
+
# State represents a partial evaluation of the series as: [sum_num, mult_num, den]
|
|
415
|
+
# Conceptually, each state translates to the following mathematical expression:
|
|
416
|
+
# (sum_num / den) + (mult_num / den) * (rest_of_the_series)
|
|
417
|
+
#
|
|
418
|
+
# The initial state [denominator, numerator, denominator] simply represents:
|
|
419
|
+
# (denominator / denominator) + (numerator / denominator) * rest
|
|
420
|
+
# = 1 + (numerator / denominator) * rest
|
|
421
|
+
#
|
|
422
|
+
fractions = (b - l + 1..b + l).map do |i|
|
|
423
|
+
denominator = (xs - i * s10) * ((i - b + l) * i)
|
|
424
|
+
numerator = (xs - (i - 1) * s10) * (-b * (b + l - i + 1))
|
|
425
|
+
[denominator, numerator, denominator]
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
while fractions.size > 1
|
|
429
|
+
fractions = fractions.each_slice(2).map do |a, c|
|
|
430
|
+
c ||= [1, 0, 1]
|
|
431
|
+
# Merge operation for BSM:
|
|
432
|
+
# a[0]/a[2] + a[1]/a[2] * (c[0]/c[2] + c[1]/c[2] * rest)
|
|
433
|
+
# = (a[0]*c[2] + a[1]*c[0]) / (a[2]*c[2]) + (a[1]*c[1]) / (a[2]*c[2]) * rest
|
|
434
|
+
v0 = a[0] * c[2] + a[1] * c[0]
|
|
435
|
+
v1 = a[1] * c[1]
|
|
436
|
+
v2 = a[2] * c[2]
|
|
437
|
+
# Drop lower bits to avoid the integers growing too large; see drop_cap_bits.
|
|
438
|
+
# All three components share the shift, so the represented ratios are unchanged.
|
|
439
|
+
s = v2.bit_length - cap
|
|
440
|
+
if s > 0
|
|
441
|
+
v0 >>= s
|
|
442
|
+
v1 >>= s
|
|
443
|
+
v2 >>= s
|
|
444
|
+
end
|
|
445
|
+
[v0, v1, v2]
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
fraction = fractions.first
|
|
449
|
+
sum = BigDecimal((fraction[0] + fraction[1]) * s10).div(fraction[2] * (xs - (b - l) * s10), prec)
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
# Reconstruct Gamma(x_original) by reversing the scaling and applying shift formula
|
|
453
|
+
e = x - (b - l)
|
|
454
|
+
if e.frac == 0.5
|
|
455
|
+
# The factorial doubling path always has x = integer + 0.5. For this exponent shape,
|
|
456
|
+
# integer power and sqrt are much cheaper than the exp/log based fractional power.
|
|
457
|
+
power_part = BigDecimal(b).power(e.to_i, prec).mult(BigDecimal(b).sqrt(prec), prec)
|
|
458
|
+
else
|
|
459
|
+
power_part = BigDecimal(b).power(e, prec)
|
|
460
|
+
end
|
|
461
|
+
base = power_part.div(prod.mult(sum, prec), prec)
|
|
462
|
+
large_factorial_arg = b - l
|
|
463
|
+
small_factorial_arg = 2 * l
|
|
464
|
+
[base, large_factorial_arg, small_factorial_arg, exp2]
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# Calculates bernoulli number.
|
|
468
|
+
# bns: calculated bernoulli numbers for memoization
|
|
469
|
+
def self.bernoulli(n, bns, prec)
|
|
470
|
+
return bns[0] ||= BigDecimal(1) if n == 0
|
|
471
|
+
return bns[1] ||= BigDecimal(-0.5) if n == 1
|
|
472
|
+
return bns[n] ||= BigDecimal(0) if n.odd?
|
|
473
|
+
bns[n] ||= (
|
|
474
|
+
comb = 1
|
|
475
|
+
sum = BigDecimal(0)
|
|
476
|
+
n.times do |i|
|
|
477
|
+
sum = sum.add(comb * bernoulli(i, bns, prec), prec)
|
|
478
|
+
comb = comb * (n - i + 1) / (i + 1)
|
|
479
|
+
end
|
|
480
|
+
sum.div(-n - 1, prec)
|
|
481
|
+
)
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
# Calculate log gamma using Stirling's asymptotic expansion.
|
|
485
|
+
# While the condition of this asymptotic expansion is x > prec * log(10) / 2 / pi,
|
|
486
|
+
# we'll use this method only when x is extremely large to reduce the cost of Bernoulli number generation.
|
|
487
|
+
def self.lgamma_stirling(x, prec)
|
|
488
|
+
x = BigDecimal(x)
|
|
489
|
+
y = (x * (BigMath.log(x, prec) - 1)).add(BigMath.log(2 * BigMath::PI(prec).div(x, prec), prec) / 2, prec)
|
|
490
|
+
bns = []
|
|
491
|
+
xn = x
|
|
492
|
+
x2 = x.mult(x, prec)
|
|
493
|
+
(1..).each do |k|
|
|
494
|
+
xn = xn.mult(x2, prec) if k != 1
|
|
495
|
+
d = bernoulli(2 * k, bns, prec).div(xn, prec).div(2 * k * (2 * k - 1), prec)
|
|
496
|
+
y = y.add(d, prec)
|
|
497
|
+
break if d.exponent < y.exponent - prec
|
|
498
|
+
end
|
|
499
|
+
y
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# Returns sin(pi * x), for gamma reflection formula calculation
|
|
503
|
+
def self.sinpix(x, pi, prec)
|
|
504
|
+
x = x % 2
|
|
505
|
+
sign = x > 1 ? -1 : 1
|
|
506
|
+
x %= 1
|
|
507
|
+
x = 1 - x if x > 0.5 # to avoid sin(pi*x) loss of precision for x close to 1
|
|
508
|
+
sign * BigMath.sin(x.mult(pi, prec), prec)
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
private_constant :Gamma
|
|
513
|
+
end
|
data/lib/bigdecimal/math.rb
CHANGED
|
@@ -596,28 +596,8 @@ module BigMath
|
|
|
596
596
|
# #=> "0.84270079294971486934122063508261e0"
|
|
597
597
|
#
|
|
598
598
|
def erf(x, prec)
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
602
|
-
return BigDecimal(x.infinite?) if x.infinite?
|
|
603
|
-
return BigDecimal(0) if x == 0
|
|
604
|
-
return -erf(-x, prec) if x < 0
|
|
605
|
-
return BigDecimal(1) if x > 5000000000 # erf(5000000000) > 1 - 1e-10000000000000000000
|
|
606
|
-
|
|
607
|
-
if x > 8
|
|
608
|
-
xf = x.to_f
|
|
609
|
-
log10_erfc = -xf ** 2 / Math.log(10) - Math.log10(xf * Math::PI ** 0.5)
|
|
610
|
-
erfc_prec = [prec + log10_erfc.ceil, 1].max
|
|
611
|
-
erfc = _erfc_asymptotic(x, erfc_prec)
|
|
612
|
-
return BigDecimal(1).sub(erfc, prec) if erfc
|
|
613
|
-
end
|
|
614
|
-
|
|
615
|
-
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
616
|
-
x_smallprec = x.mult(1, Integer.sqrt(prec2) / 2)
|
|
617
|
-
# Taylor series of x with small precision is fast
|
|
618
|
-
erf1 = _erf_taylor(x_smallprec, BigDecimal(0), BigDecimal(0), prec2)
|
|
619
|
-
# Taylor series converges quickly for small x
|
|
620
|
-
_erf_taylor(x - x_smallprec, x_smallprec, erf1, prec2).mult(1, prec)
|
|
599
|
+
require 'bigdecimal/math/erf'
|
|
600
|
+
Erf.erf(x, prec)
|
|
621
601
|
end
|
|
622
602
|
|
|
623
603
|
# call-seq:
|
|
@@ -632,87 +612,8 @@ module BigMath
|
|
|
632
612
|
# #=> "0.20884875837625447570007862949578e-44"
|
|
633
613
|
#
|
|
634
614
|
def erfc(x, prec)
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
638
|
-
return BigDecimal(1 - x.infinite?) if x.infinite?
|
|
639
|
-
return BigDecimal(1).sub(erf(x, prec + BigDecimal::Internal::EXTRA_PREC), prec) if x < 0.5
|
|
640
|
-
return BigDecimal::Internal.underflow_computation_result if x > 5000000000 # erfc(5000000000) < 1e-10000000000000000000 (underflow)
|
|
641
|
-
|
|
642
|
-
if x >= 8
|
|
643
|
-
y = _erfc_asymptotic(x, prec)
|
|
644
|
-
return y.mult(1, prec) if y
|
|
645
|
-
end
|
|
646
|
-
|
|
647
|
-
# erfc(x) = 1 - erf(x) < exp(-x**2)/x/sqrt(pi)
|
|
648
|
-
# Precision of erf(x) needs about log10(exp(-x**2)/x/sqrt(pi)) extra digits
|
|
649
|
-
log10 = 2.302585092994046
|
|
650
|
-
xf = x.to_f
|
|
651
|
-
high_prec = prec + BigDecimal::Internal::EXTRA_PREC + ((xf**2 + Math.log(xf) + Math.log(Math::PI)/2) / log10).ceil
|
|
652
|
-
BigDecimal(1).sub(erf(x, high_prec), prec)
|
|
653
|
-
end
|
|
654
|
-
|
|
655
|
-
# Calculates erf(x + a)
|
|
656
|
-
private_class_method def _erf_taylor(x, a, erf_a, prec) # :nodoc:
|
|
657
|
-
return erf_a if x.zero?
|
|
658
|
-
# Let f(x+a) = erf(x+a)*exp((x+a)**2)*sqrt(pi)/2
|
|
659
|
-
# = c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...
|
|
660
|
-
# f'(x+a) = 1+2*(x+a)*f(x+a)
|
|
661
|
-
# f'(x+a) = c1 + 2*c2*x + 3*c3*x**2 + 4*c4*x**3 + 5*c5*x**4 + ...
|
|
662
|
-
# = 1+2*(x+a)*(c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...)
|
|
663
|
-
# therefore,
|
|
664
|
-
# c0 = f(a)
|
|
665
|
-
# c1 = 2 * a * c0 + 1
|
|
666
|
-
# c2 = (2 * c0 + 2 * a * c1) / 2
|
|
667
|
-
# c3 = (2 * c1 + 2 * a * c2) / 3
|
|
668
|
-
# c4 = (2 * c2 + 2 * a * c3) / 4
|
|
669
|
-
#
|
|
670
|
-
# All coefficients are positive when a >= 0
|
|
671
|
-
|
|
672
|
-
scale = BigDecimal(2).div(sqrt(PI(prec), prec), prec)
|
|
673
|
-
c_prev = erf_a.div(scale.mult(exp(-a*a, prec), prec), prec)
|
|
674
|
-
c_next = (2 * a * c_prev).add(1, prec).mult(x, prec)
|
|
675
|
-
sum = c_prev.add(c_next, prec)
|
|
676
|
-
|
|
677
|
-
2.step do |k|
|
|
678
|
-
cn = (c_prev.mult(x, prec) + a * c_next).mult(2, prec).mult(x, prec).div(k, prec)
|
|
679
|
-
sum = sum.add(cn, prec)
|
|
680
|
-
c_prev, c_next = c_next, cn
|
|
681
|
-
break if [c_prev, c_next].all? { |c| c.zero? || (c.exponent < sum.exponent - prec) }
|
|
682
|
-
end
|
|
683
|
-
value = sum.mult(scale.mult(exp(-(x + a).mult(x + a, prec), prec), prec), prec)
|
|
684
|
-
value > 1 ? BigDecimal(1) : value
|
|
685
|
-
end
|
|
686
|
-
|
|
687
|
-
private_class_method def _erfc_asymptotic(x, prec) # :nodoc:
|
|
688
|
-
# Let f(x) = erfc(x)*sqrt(pi)*exp(x**2)/2
|
|
689
|
-
# f(x) satisfies the following differential equation:
|
|
690
|
-
# 2*x*f(x) = f'(x) + 1
|
|
691
|
-
# From the above equation, we can derive the following asymptotic expansion:
|
|
692
|
-
# f(x) = (0..kmax).sum { (-1)**k * (2*k)! / 4**k / k! / x**(2*k)) } / x
|
|
693
|
-
|
|
694
|
-
# This asymptotic expansion does not converge.
|
|
695
|
-
# But if there is a k that satisfies (2*k)! / 4**k / k! / x**(2*k) < 10**(-prec),
|
|
696
|
-
# It is enough to calculate erfc within the given precision.
|
|
697
|
-
# Using Stirling's approximation, we can simplify this condition to:
|
|
698
|
-
# sqrt(2)/2 + k*log(k) - k - 2*k*log(x) < -prec*log(10)
|
|
699
|
-
# and the left side is minimized when k = x**2.
|
|
700
|
-
prec += BigDecimal::Internal::EXTRA_PREC
|
|
701
|
-
xf = x.to_f
|
|
702
|
-
kmax = (1..(xf ** 2).floor).bsearch do |k|
|
|
703
|
-
Math.log(2) / 2 + k * Math.log(k) - k - 2 * k * Math.log(xf) < -prec * Math.log(10)
|
|
704
|
-
end
|
|
705
|
-
return unless kmax
|
|
706
|
-
|
|
707
|
-
sum = BigDecimal(1)
|
|
708
|
-
# To calculate `exp(x2, prec)`, x2 needs extra log10(x**2) digits of precision
|
|
709
|
-
x2 = x.mult(x, prec + (2 * Math.log10(xf)).ceil)
|
|
710
|
-
d = BigDecimal(1)
|
|
711
|
-
(1..kmax).each do |k|
|
|
712
|
-
d = d.div(x2, prec).mult(1 - 2 * k, prec).div(2, prec)
|
|
713
|
-
sum = sum.add(d, prec)
|
|
714
|
-
end
|
|
715
|
-
sum.div(exp(x2, prec).mult(PI(prec).sqrt(prec), prec), prec).div(x, prec)
|
|
615
|
+
require 'bigdecimal/math/erf'
|
|
616
|
+
Erf.erfc(x, prec)
|
|
716
617
|
end
|
|
717
618
|
|
|
718
619
|
# call-seq:
|
|
@@ -725,22 +626,8 @@ module BigMath
|
|
|
725
626
|
# #=> "0.17724538509055160272981674833411e1"
|
|
726
627
|
#
|
|
727
628
|
def gamma(x, prec)
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
731
|
-
if x < 0.5
|
|
732
|
-
raise Math::DomainError, 'Numerical argument is out of domain - gamma' if x.frac.zero?
|
|
733
|
-
|
|
734
|
-
# Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z)
|
|
735
|
-
pi = PI(prec2)
|
|
736
|
-
sin = _sinpix(x, pi, prec2)
|
|
737
|
-
return pi.div(gamma(1 - x, prec2).mult(sin, prec2), prec)
|
|
738
|
-
elsif x.frac.zero? && x < 1000 * prec
|
|
739
|
-
return _gamma_positive_integer(x, prec2).mult(1, prec)
|
|
740
|
-
end
|
|
741
|
-
|
|
742
|
-
a, sum = _gamma_spouge_sum_part(x, prec2)
|
|
743
|
-
(x + (a - 1)).power(x - 0.5, prec2).mult(BigMath.exp(1 - x, prec2), prec2).mult(sum, prec)
|
|
629
|
+
require 'bigdecimal/math/gamma'
|
|
630
|
+
Gamma.gamma(x, prec)
|
|
744
631
|
end
|
|
745
632
|
|
|
746
633
|
# call-seq:
|
|
@@ -753,106 +640,8 @@ module BigMath
|
|
|
753
640
|
# #=> [0.57236494292470008707171367567653e0, 1]
|
|
754
641
|
#
|
|
755
642
|
def lgamma(x, prec)
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
759
|
-
if x < 0.5
|
|
760
|
-
return [BigDecimal::INFINITY, 1] if x.frac.zero?
|
|
761
|
-
|
|
762
|
-
loop do
|
|
763
|
-
# Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z)
|
|
764
|
-
pi = PI(prec2)
|
|
765
|
-
sin = _sinpix(x, pi, prec2)
|
|
766
|
-
log_gamma = BigMath.log(pi, prec2).sub(lgamma(1 - x, prec2).first + BigMath.log(sin.abs, prec2), prec)
|
|
767
|
-
return [log_gamma, sin > 0 ? 1 : -1] if prec2 + log_gamma.exponent > prec + BigDecimal::Internal::EXTRA_PREC
|
|
768
|
-
|
|
769
|
-
# Retry with higher precision if loss of significance is too large
|
|
770
|
-
prec2 = prec2 * 3 / 2
|
|
771
|
-
end
|
|
772
|
-
elsif x.frac.zero? && x < 1000 * prec
|
|
773
|
-
log_gamma = BigMath.log(_gamma_positive_integer(x, prec2), prec)
|
|
774
|
-
[log_gamma, 1]
|
|
775
|
-
else
|
|
776
|
-
# if x is close to 1 or 2, increase precision to reduce loss of significance
|
|
777
|
-
diff1_exponent = (x - 1).exponent
|
|
778
|
-
diff2_exponent = (x - 2).exponent
|
|
779
|
-
extremely_near_one = diff1_exponent < -prec2
|
|
780
|
-
extremely_near_two = diff2_exponent < -prec2
|
|
781
|
-
|
|
782
|
-
if extremely_near_one || extremely_near_two
|
|
783
|
-
# If x is extreamely close to base = 1 or 2, linear interpolation is accurate enough.
|
|
784
|
-
# Taylor expansion at x = base is: (x - base) * digamma(base) + (x - base) ** 2 * trigamma(base) / 2 + ...
|
|
785
|
-
# And we can ignore (x - base) ** 2 and higher order terms.
|
|
786
|
-
base = extremely_near_one ? 1 : 2
|
|
787
|
-
d = BigDecimal(1)._decimal_shift(1 - prec2)
|
|
788
|
-
log_gamma_d, sign = lgamma(base + d, prec2)
|
|
789
|
-
return [log_gamma_d.mult(x - base, prec2).div(d, prec), sign]
|
|
790
|
-
end
|
|
791
|
-
|
|
792
|
-
prec2 += [-diff1_exponent, -diff2_exponent, 0].max
|
|
793
|
-
a, sum = _gamma_spouge_sum_part(x, prec2)
|
|
794
|
-
log_gamma = BigMath.log(sum, prec2).add((x - 0.5).mult(BigMath.log(x.add(a - 1, prec2), prec2), prec2) + 1 - x, prec)
|
|
795
|
-
[log_gamma, 1]
|
|
796
|
-
end
|
|
797
|
-
end
|
|
798
|
-
|
|
799
|
-
# Returns sum part: sqrt(2*pi) and c[k]/(x+k) terms of Spouge's approximation
|
|
800
|
-
private_class_method def _gamma_spouge_sum_part(x, prec) # :nodoc:
|
|
801
|
-
x -= 1
|
|
802
|
-
# Spouge's approximation
|
|
803
|
-
# x! = (x + a)**(x + 0.5) * exp(-x - a) * (sqrt(2 * pi) + (1..a - 1).sum{|k| c[k] / (x + k) } + epsilon)
|
|
804
|
-
# where c[k] = (-1)**k * (a - k)**(k - 0.5) * exp(a - k) / (k - 1)!
|
|
805
|
-
# and epsilon is bounded by a**(-0.5) * (2 * pi) ** (-a - 0.5)
|
|
806
|
-
|
|
807
|
-
# Estimate required a for given precision
|
|
808
|
-
a = (prec / Math.log10(2 * Math::PI)).ceil
|
|
809
|
-
|
|
810
|
-
# Calculate exponent of c[k] in low precision to estimate required precision
|
|
811
|
-
low_prec = 16
|
|
812
|
-
log10f = Math.log(10)
|
|
813
|
-
x_low_prec = x.mult(1, low_prec)
|
|
814
|
-
loggamma_k = 0
|
|
815
|
-
ck_exponents = (1..a-1).map do |k|
|
|
816
|
-
loggamma_k += Math.log10(k - 1) if k > 1
|
|
817
|
-
-loggamma_k - k / log10f + (k - 0.5) * Math.log10(a - k) - BigDecimal::Internal.float_log(x_low_prec.add(k, low_prec)) / log10f
|
|
818
|
-
end
|
|
819
|
-
|
|
820
|
-
# Estimate exponent of sum by Stirling's approximation
|
|
821
|
-
approx_sum_exponent = x < 1 ? -Math.log10(a) / 2 : Math.log10(2 * Math::PI) / 2 + x_low_prec.add(0.5, low_prec) * Math.log10(x_low_prec / x_low_prec.add(a, low_prec))
|
|
822
|
-
|
|
823
|
-
# Determine required precision of c[k]
|
|
824
|
-
prec2 = [ck_exponents.max.ceil - approx_sum_exponent.floor, 0].max + prec
|
|
825
|
-
|
|
826
|
-
einv = BigMath.exp(-1, prec2)
|
|
827
|
-
sum = (PI(prec) * 2).sqrt(prec).mult(BigMath.exp(-a, prec), prec)
|
|
828
|
-
y = BigDecimal(1)
|
|
829
|
-
(1..a - 1).each do |k|
|
|
830
|
-
# c[k] = (-1)**k * (a - k)**(k - 0.5) * exp(-k) / (k-1)! / (x + k)
|
|
831
|
-
y = y.div(1 - k, prec2) if k > 1
|
|
832
|
-
y = y.mult(einv, prec2)
|
|
833
|
-
z = y.mult(BigDecimal((a - k) ** k), prec2).div(BigDecimal(a - k).sqrt(prec2).mult(x.add(k, prec2), prec2), prec2)
|
|
834
|
-
# sum += c[k] / (x + k)
|
|
835
|
-
sum = sum.add(z, prec2)
|
|
836
|
-
end
|
|
837
|
-
[a, sum]
|
|
838
|
-
end
|
|
839
|
-
|
|
840
|
-
private_class_method def _gamma_positive_integer(x, prec) # :nodoc:
|
|
841
|
-
return x if x == 1
|
|
842
|
-
numbers = (1..x - 1).map {|i| BigDecimal(i) }
|
|
843
|
-
while numbers.size > 1
|
|
844
|
-
numbers = numbers.each_slice(2).map {|a, b| b ? a.mult(b, prec) : a }
|
|
845
|
-
end
|
|
846
|
-
numbers.first
|
|
847
|
-
end
|
|
848
|
-
|
|
849
|
-
# Returns sin(pi * x), for gamma reflection formula calculation
|
|
850
|
-
private_class_method def _sinpix(x, pi, prec) # :nodoc:
|
|
851
|
-
x = x % 2
|
|
852
|
-
sign = x > 1 ? -1 : 1
|
|
853
|
-
x %= 1
|
|
854
|
-
x = 1 - x if x > 0.5 # to avoid sin(pi*x) loss of precision for x close to 1
|
|
855
|
-
sign * sin(x.mult(pi, prec), prec)
|
|
643
|
+
require 'bigdecimal/math/gamma'
|
|
644
|
+
Gamma.lgamma(x, prec)
|
|
856
645
|
end
|
|
857
646
|
|
|
858
647
|
# call-seq:
|
data/sig/big_decimal.rbs
CHANGED
|
@@ -1237,7 +1237,9 @@ module Kernel
|
|
|
1237
1237
|
# Raises an exception if `value` evaluates to a Float and `digits` is larger
|
|
1238
1238
|
# than Float::DIG + 1.
|
|
1239
1239
|
#
|
|
1240
|
-
def self?.BigDecimal: (real | string | BigDecimal initial, ?int digits, ?exception:
|
|
1240
|
+
def self?.BigDecimal: (real | string | BigDecimal initial, ?int digits, ?exception: true) -> BigDecimal
|
|
1241
|
+
| (real | string | BigDecimal initial, ?int digits, exception: bool) -> BigDecimal?
|
|
1242
|
+
| (untyped initial, ?untyped digits, ?exception: bool) -> BigDecimal?
|
|
1241
1243
|
end
|
|
1242
1244
|
|
|
1243
1245
|
%a{annotate:rdoc:skip}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bigdecimal
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.1.
|
|
4
|
+
version: 4.1.3
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- Kenta Murata
|
|
@@ -25,6 +25,8 @@ files:
|
|
|
25
25
|
- lib/bigdecimal/jacobian.rb
|
|
26
26
|
- lib/bigdecimal/ludcmp.rb
|
|
27
27
|
- lib/bigdecimal/math.rb
|
|
28
|
+
- lib/bigdecimal/math/erf.rb
|
|
29
|
+
- lib/bigdecimal/math/gamma.rb
|
|
28
30
|
- lib/bigdecimal/newton.rb
|
|
29
31
|
- lib/bigdecimal/util.rb
|
|
30
32
|
- sample/linear.rb
|