distribution 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +12 -9
- data/Gemfile +1 -3
- data/History.txt +13 -10
- data/README.md +37 -30
- data/Rakefile +14 -20
- data/distribution.gemspec +14 -8
- data/lib/distribution.rb +60 -70
- data/lib/distribution/binomial/ruby.rb +4 -2
- data/lib/distribution/hypergeometric/gsl.rb +1 -1
- data/lib/distribution/hypergeometric/ruby.rb +2 -2
- data/lib/distribution/math_extension.rb +193 -231
- data/lib/distribution/version.rb +1 -1
- data/spec/beta_spec.rb +8 -8
- data/spec/binomial_spec.rb +13 -9
- data/spec/chisquare_spec.rb +3 -3
- data/spec/distribution_spec.rb +4 -4
- data/spec/exponential_spec.rb +1 -1
- data/spec/gamma_spec.rb +1 -1
- data/spec/math_extension_spec.rb +1 -2
- data/spec/poisson_spec.rb +1 -1
- data/spec/spec_helper.rb +17 -2
- metadata +23 -9
- data/Manifest.txt +0 -105
@@ -4,7 +4,7 @@ module Distribution
|
|
4
4
|
class << self
|
5
5
|
def pdf(k,n,pr)
|
6
6
|
raise "k>n" if k>n
|
7
|
-
|
7
|
+
Math.binomial_coefficient(n,k)*(pr**k)*(1-pr)**(n-k)
|
8
8
|
end
|
9
9
|
# TODO: Use exact_regularized_beta for
|
10
10
|
# small values and regularized_beta for bigger ones.
|
@@ -13,7 +13,9 @@ module Distribution
|
|
13
13
|
Math.regularized_beta(1-pr,n - k,k+1)
|
14
14
|
end
|
15
15
|
def exact_cdf(k,n,pr)
|
16
|
-
(0..k).inject(0) {|ac,i| ac+pdf(i,n,pr)}
|
16
|
+
out=(0..k).inject(0) {|ac,i| ac+pdf(i,n,pr)}
|
17
|
+
out=1 if out>1.0
|
18
|
+
return out
|
17
19
|
end
|
18
20
|
def p_value(prob,n,pr)
|
19
21
|
ac=0
|
@@ -47,8 +47,8 @@ module Distribution
|
|
47
47
|
#
|
48
48
|
# Slow, but secure
|
49
49
|
def cdf(k, m, n, total)
|
50
|
-
raise "k>m" if k>m
|
51
|
-
raise "k>n" if k>n
|
50
|
+
raise(ArgumentError, "k>m") if k>m
|
51
|
+
raise(ArgumentError, "k>n") if k>n
|
52
52
|
# Store the den
|
53
53
|
den=bc(total,n)
|
54
54
|
(0..k).collect { |ki| pdf_with_den(ki,m,n,total,den) }.inject { |sum,v| sum+v}
|
@@ -1,282 +1,250 @@
|
|
1
|
-
# The next few requires eventually probably need to go in their own gem. They're all functions and constants used by
|
2
|
-
# GSL-adapted pure Ruby math functions.
|
3
|
-
require "distribution/math_extension/chebyshev_series"
|
4
|
-
require "distribution/math_extension/erfc"
|
5
|
-
require "distribution/math_extension/exponential_integral"
|
6
|
-
require "distribution/math_extension/gammastar"
|
7
|
-
require "distribution/math_extension/gsl_utilities"
|
8
|
-
require "distribution/math_extension/incomplete_gamma"
|
9
|
-
require "distribution/math_extension/incomplete_beta"
|
10
|
-
require "distribution/math_extension/log_utilities"
|
11
|
-
|
12
|
-
|
13
|
-
if RUBY_VERSION<"1.9"
|
14
|
-
require 'mathn'
|
15
|
-
def Prime.each(upper,&block)
|
16
|
-
@primes=Prime.new
|
17
|
-
@primes.each do |prime|
|
18
|
-
break if prime > upper.to_i
|
19
|
-
block.call(prime)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
else
|
23
|
-
require 'prime'
|
24
|
-
end
|
25
|
-
|
26
1
|
require 'bigdecimal'
|
27
2
|
require 'bigdecimal/math'
|
3
|
+
require 'prime'
|
4
|
+
|
5
|
+
# The next few requires eventually probably need to go in their own gem. They're all functions and constants used by
|
6
|
+
# GSL-adapted pure Ruby math functions.
|
7
|
+
require 'distribution/math_extension/chebyshev_series'
|
8
|
+
require 'distribution/math_extension/erfc'
|
9
|
+
require 'distribution/math_extension/exponential_integral'
|
10
|
+
require 'distribution/math_extension/gammastar'
|
11
|
+
require 'distribution/math_extension/gsl_utilities'
|
12
|
+
require 'distribution/math_extension/incomplete_gamma'
|
13
|
+
require 'distribution/math_extension/incomplete_beta'
|
14
|
+
require 'distribution/math_extension/log_utilities'
|
28
15
|
|
29
16
|
module Distribution
|
30
|
-
# Extension for Ruby18
|
31
|
-
# Includes gamma and lgamma
|
32
|
-
module MathExtension18
|
33
|
-
LOG_2PI = Math.log(2 * Math::PI)# log(2PI)
|
34
|
-
N = 8
|
35
|
-
B0 = 1.0
|
36
|
-
B1 = -1.0 / 2.0
|
37
|
-
B2 = 1.0 / 6.0
|
38
|
-
B4 = -1.0 / 30.0
|
39
|
-
B6 = 1.0 / 42.0
|
40
|
-
B8 = -1.0 / 30.0
|
41
|
-
B10 = 5.0 / 66.0
|
42
|
-
B12 = -691.0 / 2730.0
|
43
|
-
B14 = 7.0 / 6.0
|
44
|
-
B16 = -3617.0 / 510.0
|
45
|
-
|
46
|
-
|
47
|
-
# From statistics2
|
48
|
-
def loggamma(x)
|
49
|
-
v = 1.0
|
50
|
-
while (x < N)
|
51
|
-
v *= x
|
52
|
-
x += 1.0
|
53
|
-
end
|
54
|
-
w = 1.0 / (x * x)
|
55
|
-
ret = B16 / (16 * 15)
|
56
|
-
ret = ret * w + B14 / (14 * 13)
|
57
|
-
ret = ret * w + B12 / (12 * 11)
|
58
|
-
ret = ret * w + B10 / (10 * 9)
|
59
|
-
ret = ret * w + B8 / ( 8 * 7)
|
60
|
-
ret = ret * w + B6 / ( 6 * 5)
|
61
|
-
ret = ret * w + B4 / ( 4 * 3)
|
62
|
-
ret = ret * w + B2 / ( 2 * 1)
|
63
|
-
ret = ret / x + 0.5 * LOG_2PI - Math.log(v) - x + (x - 0.5) * Math.log(x)
|
64
|
-
ret
|
65
|
-
end
|
66
17
|
|
67
|
-
# Gamma function.
|
68
|
-
# From statistics2
|
69
|
-
def gamma(x)
|
70
|
-
if (x < 0.0)
|
71
|
-
return Math::PI / (Math.sin(Math::PI * x) * Math.exp(loggamma(1 - x))) #/
|
72
|
-
end
|
73
|
-
Math.exp(loggamma(x))
|
74
|
-
end
|
75
|
-
def lgamma(x)
|
76
|
-
[loggamma(x.abs), Math.gamma(x) < 0 ? -1 : 1]
|
77
|
-
end
|
78
|
-
end
|
79
18
|
# Useful additions to Math
|
80
19
|
module MathExtension
|
20
|
+
|
81
21
|
# Factorization based on Prime Swing algorithm, by Luschny (the king of factorial numbers analysis :P )
|
82
22
|
# == Reference
|
83
23
|
# * The Homepage of Factorial Algorithms. (C) Peter Luschny, 2000-2010
|
84
24
|
# == URL: http://www.luschny.de/math/factorial/csharp/FactorialPrimeSwing.cs.html
|
85
25
|
class SwingFactorial
|
26
|
+
|
27
|
+
SmallOddSwing = [1, 1, 1, 3, 3, 15, 5, 35, 35, 315, 63, 693, 231, 3003,
|
28
|
+
429, 6435, 6435, 109_395, 12_155, 230_945, 46_189,
|
29
|
+
969_969, 88_179, 2_028_117, 676_039, 16_900_975,
|
30
|
+
1_300_075, 35_102_025, 5_014_575, 145_422_675,
|
31
|
+
9_694_845, 300_540_195, 300_540_195]
|
32
|
+
|
33
|
+
SmallFactorial = [1, 1, 2, 6, 24, 120, 720, 5040, 40_320, 362_880,
|
34
|
+
3_628_800, 39_916_800, 479_001_600, 6_227_020_800,
|
35
|
+
87_178_291_200, 1_307_674_368_000, 20_922_789_888_000,
|
36
|
+
355_687_428_096_000, 6_402_373_705_728_000,
|
37
|
+
121_645_100_408_832_000, 2_432_902_008_176_640_000]
|
38
|
+
|
86
39
|
attr_reader :result
|
87
|
-
|
88
|
-
SmallFactorial=[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000, 355687428096000, 6402373705728000, 121645100408832000, 2432902008176640000]
|
40
|
+
|
89
41
|
def bitcount(n)
|
90
|
-
bc = n - ((n >> 1) & 0x55555555)
|
91
|
-
bc = (bc & 0x33333333) + ((bc >> 2) & 0x33333333)
|
92
|
-
bc = (bc + (bc >> 4)) & 0x0f0f0f0f
|
93
|
-
bc += bc >> 8
|
94
|
-
bc += bc >> 16
|
95
|
-
bc
|
42
|
+
bc = n - ((n >> 1) & 0x55555555)
|
43
|
+
bc = (bc & 0x33333333) + ((bc >> 2) & 0x33333333)
|
44
|
+
bc = (bc + (bc >> 4)) & 0x0f0f0f0f
|
45
|
+
bc += bc >> 8
|
46
|
+
bc += bc >> 16
|
47
|
+
bc &= 0x3f
|
96
48
|
bc
|
97
49
|
end
|
50
|
+
|
98
51
|
def initialize(n)
|
99
|
-
if
|
100
|
-
@result=SmallFactorial[n]
|
101
|
-
#naive_factorial(n)
|
52
|
+
if n < 20
|
53
|
+
@result = SmallFactorial[n]
|
54
|
+
# naive_factorial(n)
|
102
55
|
else
|
103
|
-
|
104
|
-
|
105
|
-
|
56
|
+
@prime_list = []
|
57
|
+
exp2 = n - bitcount(n)
|
58
|
+
@result = recfactorial(n) << exp2
|
106
59
|
end
|
107
60
|
end
|
61
|
+
|
108
62
|
def recfactorial(n)
|
109
|
-
return 1 if n<2
|
110
|
-
(recfactorial(n/2)**2) * swing(n)
|
63
|
+
return 1 if n < 2
|
64
|
+
(recfactorial(n / 2)**2) * swing(n)
|
111
65
|
end
|
66
|
+
|
112
67
|
def swing(n)
|
113
|
-
return SmallOddSwing[n] if
|
68
|
+
return SmallOddSwing[n] if n < 33
|
114
69
|
sqrtN = Math.sqrt(n).floor
|
115
|
-
count=0
|
116
|
-
|
117
|
-
Prime.each(n/3) do |prime|
|
118
|
-
next if prime<3
|
119
|
-
if (prime<=sqrtN)
|
120
|
-
q=n
|
121
|
-
_p=1
|
122
|
-
|
123
|
-
while(
|
124
|
-
if
|
125
|
-
_p*=prime
|
70
|
+
count = 0
|
71
|
+
|
72
|
+
Prime.each(n / 3) do |prime|
|
73
|
+
next if prime < 3
|
74
|
+
if (prime <= sqrtN)
|
75
|
+
q = n
|
76
|
+
_p = 1
|
77
|
+
|
78
|
+
while (q = (q / prime).truncate) > 0
|
79
|
+
if q.odd?
|
80
|
+
_p *= prime
|
126
81
|
end
|
127
82
|
end
|
128
|
-
if _p>1
|
129
|
-
@prime_list[count]=_p
|
130
|
-
count+=1
|
83
|
+
if _p > 1
|
84
|
+
@prime_list[count] = _p
|
85
|
+
count += 1
|
131
86
|
end
|
132
|
-
|
87
|
+
|
133
88
|
else
|
134
|
-
if (
|
135
|
-
@prime_list[count]=prime
|
136
|
-
count+=1
|
89
|
+
if (n / prime).truncate.odd?
|
90
|
+
@prime_list[count] = prime
|
91
|
+
count += 1
|
137
92
|
end
|
138
93
|
end
|
139
94
|
end
|
140
|
-
prod=get_primorial((n/2).truncate+1,n)
|
141
|
-
prod * @prime_list[0,count].inject(1) {|ac,v| ac*v}
|
95
|
+
prod = get_primorial((n / 2).truncate + 1, n)
|
96
|
+
prod * @prime_list[0, count].inject(1) { |ac, v| ac * v }
|
142
97
|
end
|
143
|
-
|
144
|
-
|
98
|
+
|
99
|
+
def get_primorial(low, up)
|
100
|
+
prod = 1
|
145
101
|
Prime.each(up) do |prime|
|
146
|
-
next if prime<low
|
147
|
-
prod*=prime
|
102
|
+
next if prime < low
|
103
|
+
prod *= prime
|
148
104
|
end
|
149
105
|
prod
|
150
106
|
end
|
107
|
+
|
151
108
|
def naive_factorial(n)
|
152
|
-
@result=(self.class).naive_factorial(n)
|
109
|
+
@result = (self.class).naive_factorial(n)
|
153
110
|
end
|
111
|
+
|
154
112
|
def self.naive_factorial(n)
|
155
|
-
(2..n).inject(1) { |f,nn| f * nn }
|
113
|
+
(2..n).inject(1) { |f, nn| f * nn }
|
156
114
|
end
|
157
115
|
end
|
116
|
+
|
158
117
|
# Module to calculate approximated factorial
|
159
118
|
# Based (again) on Luschny formula, with 16 digits of precision
|
160
119
|
# == Reference
|
161
120
|
# * http://www.luschny.de/math/factorial/approx/SimpleCases.html
|
162
121
|
module ApproxFactorial
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
122
|
+
|
123
|
+
class << self
|
124
|
+
def stieltjes_ln_factorial(z)
|
125
|
+
a0 = 1.quo(12); a1 = 1.quo(30); a2 = 53.quo(210); a3 = 195.quo(371)
|
126
|
+
a4 = 22_999.quo(22_737); a5 = 29_944_523.quo(19_733_142)
|
127
|
+
a6 = 109_535_241_009.quo(48_264_275_462)
|
128
|
+
zz = z + 1
|
129
|
+
|
130
|
+
(1.quo(2)) * Math.log(2 * Math::PI) + (zz - 1.quo(2)) * Math.log(zz) - zz +
|
131
|
+
a0.quo(zz + a1.quo(zz + a2.quo(zz + a3.quo(zz + a4.quo(zz + a5.quo(zz + a6.quo(zz)))))))
|
132
|
+
end
|
133
|
+
|
134
|
+
def stieltjes_ln_factorial_big(z)
|
135
|
+
a0 = 1 / 12.0; a1 = 1 / 30.0; a2 = 53 / 210.0; a3 = 195 / 371.0
|
136
|
+
a4 = 22_999 / 22_737.0; a5 = 29_944_523 / 19_733_142.0
|
137
|
+
a6 = 109_535_241_009 / 48_264_275_462.0
|
138
|
+
zz = z + 1
|
139
|
+
|
140
|
+
BigDecimal('0.5') * BigMath.log(BigDecimal('2') * BigMath::PI(20), 20) + BigDecimal((zz - 0.5).to_s) * BigMath.log(BigDecimal(zz.to_s), 20) - BigDecimal(zz.to_s) + BigDecimal((
|
141
|
+
a0 / (zz + a1 / (zz + a2 / (zz + a3 / (zz + a4 / (zz + a5 / (zz + a6 / zz))))))
|
142
|
+
).to_s)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Valid upto 11 digits
|
146
|
+
def stieltjes_factorial(x)
|
147
|
+
y = x
|
148
|
+
_p = 1
|
149
|
+
|
150
|
+
while y < 8
|
151
|
+
_p *= y; y += 1
|
191
152
|
end
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
r
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
153
|
+
|
154
|
+
lr = stieltjes_ln_factorial(y)
|
155
|
+
r = Math.exp(lr)
|
156
|
+
|
157
|
+
if r.infinite?
|
158
|
+
r = BigMath.exp(BigDecimal(lr.to_s), 20)
|
159
|
+
r = (r * x) / (_p * y) if x < 8
|
160
|
+
r = r.to_i
|
161
|
+
else
|
162
|
+
r = (r * x) / (_p * y) if x < 8
|
163
|
+
end
|
164
|
+
r
|
201
165
|
end
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Exact factorial.
|
170
|
+
# Use lookup on a Hash table on n<20
|
171
|
+
# and Prime Swing algorithm for higher values.
|
208
172
|
def factorial(n)
|
209
173
|
SwingFactorial.new(n).result
|
210
174
|
end
|
175
|
+
|
211
176
|
# Approximate factorial, up to 16 digits
|
212
177
|
# Based of Luschy algorithm
|
213
178
|
def fast_factorial(n)
|
214
179
|
ApproxFactorial.stieltjes_factorial(n)
|
215
180
|
end
|
216
|
-
|
181
|
+
|
217
182
|
# Beta function.
|
218
183
|
# Source:
|
219
184
|
# * http://mathworld.wolfram.com/BetaFunction.html
|
220
|
-
def beta(x,y)
|
221
|
-
(gamma(x)*gamma(y)).quo(gamma(x+y))
|
185
|
+
def beta(x, y)
|
186
|
+
(gamma(x) * gamma(y)).quo(gamma(x + y))
|
222
187
|
end
|
223
188
|
|
224
189
|
# Get pure-Ruby logbeta
|
225
|
-
def logbeta(x,y)
|
226
|
-
Beta.log_beta(x,y).first
|
190
|
+
def logbeta(x, y)
|
191
|
+
Beta.log_beta(x, y).first
|
227
192
|
end
|
228
193
|
|
229
194
|
# Log beta function conforming to style of lgamma (returns sign in second array index)
|
230
|
-
def lbeta(x,y)
|
231
|
-
Beta.log_beta(x,y)
|
195
|
+
def lbeta(x, y)
|
196
|
+
Beta.log_beta(x, y)
|
232
197
|
end
|
233
198
|
|
234
199
|
# I_x(a,b): Regularized incomplete beta function
|
235
200
|
# Fast version. For a exact calculation, based on factorial
|
236
201
|
# use exact_regularized_beta_function
|
237
|
-
def regularized_beta(x,a,b)
|
238
|
-
return 1 if x==1
|
239
|
-
IncompleteBeta.evaluate(a,b,x)
|
202
|
+
def regularized_beta(x, a, b)
|
203
|
+
return 1 if x == 1
|
204
|
+
IncompleteBeta.evaluate(a, b, x)
|
240
205
|
end
|
206
|
+
|
241
207
|
# I_x(a,b): Regularized incomplete beta function
|
242
208
|
# TODO: Find a faster version.
|
243
209
|
# Source:
|
244
210
|
# * http://dlmf.nist.gov/8.17
|
245
|
-
def exact_regularized_beta(x,a,b)
|
246
|
-
return 1 if x==1
|
247
|
-
|
248
|
-
|
249
|
-
(
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
211
|
+
def exact_regularized_beta(x, a, b)
|
212
|
+
return 1 if x == 1
|
213
|
+
|
214
|
+
m = a.to_i
|
215
|
+
n = (b + a - 1).to_i
|
216
|
+
|
217
|
+
(m..n).inject(0) do|sum, j|
|
218
|
+
sum + (binomial_coefficient(n, j) * x**j * (1 - x)**(n - j))
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
|
255
223
|
# Incomplete beta function: B(x;a,b)
|
256
|
-
# +a+ and +b+ are parameters and +x+ is
|
224
|
+
# +a+ and +b+ are parameters and +x+ is
|
257
225
|
# integration upper limit.
|
258
|
-
def incomplete_beta(x,a,b)
|
259
|
-
IncompleteBeta.evaluate(a,b,x)*beta(a,b)
|
260
|
-
#Math::IncompleteBeta.axpy(1.0, 0.0, a,b,x)
|
226
|
+
def incomplete_beta(x, a, b)
|
227
|
+
IncompleteBeta.evaluate(a, b, x) * beta(a, b)
|
228
|
+
# Math::IncompleteBeta.axpy(1.0, 0.0, a,b,x)
|
261
229
|
end
|
262
|
-
|
230
|
+
|
263
231
|
# Rising factorial
|
264
|
-
def rising_factorial(x,n)
|
265
|
-
factorial(x+n-1).quo(factorial(x-1))
|
232
|
+
def rising_factorial(x, n)
|
233
|
+
factorial(x + n - 1).quo(factorial(x - 1))
|
266
234
|
end
|
267
|
-
|
235
|
+
|
268
236
|
# Ln of gamma
|
269
237
|
def loggamma(x)
|
270
238
|
Math.lgamma(x).first
|
271
239
|
end
|
272
240
|
|
273
241
|
def incomplete_gamma(a, x = 0, with_error = false)
|
274
|
-
IncompleteGamma.p(a,x, with_error)
|
242
|
+
IncompleteGamma.p(a, x, with_error)
|
275
243
|
end
|
276
|
-
|
244
|
+
alias_method :gammp, :incomplete_gamma
|
277
245
|
|
278
246
|
def gammq(a, x, with_error = false)
|
279
|
-
IncompleteGamma.q(a,x,with_error)
|
247
|
+
IncompleteGamma.q(a, x, with_error)
|
280
248
|
end
|
281
249
|
|
282
250
|
def unnormalized_incomplete_gamma(a, x, with_error = false)
|
@@ -287,33 +255,33 @@ module Distribution
|
|
287
255
|
def erfc_e(x, with_error = false)
|
288
256
|
Erfc.evaluate(x, with_error)
|
289
257
|
end
|
290
|
-
|
258
|
+
|
291
259
|
# Sequences without repetition. n^k'
|
292
260
|
# Also called 'failing factorial'
|
293
|
-
def permutations(n,k)
|
294
|
-
return 1 if k==0
|
295
|
-
return n if k==1
|
296
|
-
return factorial(n) if k==n
|
297
|
-
(((n-k+1)..n).inject(1) {|ac,v| ac * v})
|
298
|
-
#factorial(x).quo(factorial(x-n))
|
261
|
+
def permutations(n, k)
|
262
|
+
return 1 if k == 0
|
263
|
+
return n if k == 1
|
264
|
+
return factorial(n) if k == n
|
265
|
+
(((n - k + 1)..n).inject(1) { |ac, v| ac * v })
|
266
|
+
# factorial(x).quo(factorial(x-n))
|
299
267
|
end
|
300
|
-
|
268
|
+
|
301
269
|
# Binomial coeffients, or:
|
302
270
|
# ( n )
|
303
271
|
# ( k )
|
304
272
|
#
|
305
273
|
# Gives the number of *different* k size subsets of a set size n
|
306
|
-
#
|
274
|
+
#
|
307
275
|
# Uses:
|
308
276
|
#
|
309
277
|
# (n) n^k' (n)..(n-k+1)
|
310
278
|
# ( ) = ---- = ------------
|
311
279
|
# (k) k! k!
|
312
280
|
#
|
313
|
-
def binomial_coefficient(n,k)
|
314
|
-
return 1 if
|
315
|
-
k=[k, n-k].min
|
316
|
-
permutations(n,k).quo(factorial(k))
|
281
|
+
def binomial_coefficient(n, k)
|
282
|
+
return 1 if k == 0 || k == n
|
283
|
+
k = [k, n - k].min
|
284
|
+
permutations(n, k).quo(factorial(k))
|
317
285
|
# The factorial way is
|
318
286
|
# factorial(n).quo(factorial(k)*(factorial(n-k)))
|
319
287
|
# The multiplicative way is
|
@@ -322,54 +290,48 @@ module Distribution
|
|
322
290
|
# Binomial coefficient using multiplicative algorithm
|
323
291
|
# On benchmarks, is faster that raising factorial method
|
324
292
|
# when k is little. Use only when you're sure of that.
|
325
|
-
def binomial_coefficient_multiplicative(n,k)
|
326
|
-
return 1 if
|
327
|
-
k=[k, n-k].min
|
328
|
-
(1..k).inject(1) {|ac, i| (ac*(n-k+i).quo(i))}
|
293
|
+
def binomial_coefficient_multiplicative(n, k)
|
294
|
+
return 1 if k == 0 || k == n
|
295
|
+
k = [k, n - k].min
|
296
|
+
(1..k).inject(1) { |ac, i| (ac * (n - k + i).quo(i)) }
|
329
297
|
end
|
330
|
-
|
298
|
+
|
331
299
|
# Approximate binomial coefficient, using gamma function.
|
332
300
|
# The fastest method, until we fall on BigDecimal!
|
333
|
-
def binomial_coefficient_gamma(n,k)
|
334
|
-
return 1 if
|
335
|
-
k=[k, n-k].min
|
301
|
+
def binomial_coefficient_gamma(n, k)
|
302
|
+
return 1 if k == 0 || k == n
|
303
|
+
k = [k, n - k].min
|
336
304
|
# First, we try direct gamma calculation for max precission
|
337
305
|
|
338
|
-
val=gamma(n + 1).quo(gamma(k+1)*gamma(n-k+1))
|
306
|
+
val = gamma(n + 1).quo(gamma(k + 1) * gamma(n - k + 1))
|
339
307
|
# Ups. Outside float point range. We try with logs
|
340
|
-
if
|
341
|
-
#puts "nan"
|
342
|
-
lg=loggamma(
|
343
|
-
val=Math.exp(lg)
|
308
|
+
if val.nan?
|
309
|
+
# puts "nan"
|
310
|
+
lg = loggamma(n + 1) - (loggamma(k + 1) + loggamma(n - k + 1))
|
311
|
+
val = Math.exp(lg)
|
344
312
|
# Crash again! We require BigDecimals
|
345
313
|
if val.infinite?
|
346
|
-
#puts "infinite"
|
347
|
-
val=BigMath.exp(BigDecimal(lg.to_s),16)
|
314
|
+
# puts "infinite"
|
315
|
+
val = BigMath.exp(BigDecimal(lg.to_s), 16)
|
348
316
|
end
|
349
317
|
end
|
350
318
|
val
|
351
319
|
end
|
352
|
-
|
320
|
+
alias_method :combinations, :binomial_coefficient
|
353
321
|
end
|
354
322
|
end
|
355
323
|
|
356
324
|
module Math
|
357
325
|
include Distribution::MathExtension
|
358
|
-
module_function :factorial, :beta, :loggamma, :erfc_e, :unnormalized_incomplete_gamma, :incomplete_gamma, :gammp, :gammq, :binomial_coefficient, :binomial_coefficient_gamma, :exact_regularized_beta, :incomplete_beta, :regularized_beta, :permutations, :rising_factorial
|
326
|
+
module_function :factorial, :beta, :loggamma, :erfc_e, :unnormalized_incomplete_gamma, :incomplete_gamma, :gammp, :gammq, :binomial_coefficient, :binomial_coefficient_gamma, :exact_regularized_beta, :incomplete_beta, :regularized_beta, :permutations, :rising_factorial, :fast_factorial, :combinations, :logbeta, :lbeta
|
359
327
|
end
|
360
328
|
|
361
329
|
# Necessary on Ruby 1.9
|
362
330
|
module CMath # :nodoc:
|
363
331
|
include Distribution::MathExtension
|
364
|
-
module_function :factorial, :beta, :loggamma, :unnormalized_incomplete_gamma,
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
remove_method :loggamma
|
370
|
-
include Distribution::MathExtension18
|
371
|
-
module_function :gamma, :loggamma, :lgamma
|
372
|
-
end
|
332
|
+
module_function :factorial, :beta, :loggamma, :unnormalized_incomplete_gamma,
|
333
|
+
:incomplete_gamma, :gammp, :gammq, :erfc_e, :binomial_coefficient,
|
334
|
+
:binomial_coefficient_gamma, :incomplete_beta, :exact_regularized_beta,
|
335
|
+
:regularized_beta, :permutations, :rising_factorial, :fast_factorial,
|
336
|
+
:combinations, :logbeta, :lbeta
|
373
337
|
end
|
374
|
-
|
375
|
-
|