distribution 0.7.1 → 0.7.2
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/.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
|
-
|