distribution 0.6.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.travis.yml +13 -0
- data/.yardopts +5 -0
- data/Gemfile +5 -0
- data/History.txt +24 -8
- data/LICENCE.md +26 -0
- data/README.md +155 -0
- data/Rakefile +15 -19
- data/benchmark/binomial_coefficient.rb +19 -23
- data/benchmark/binomial_coefficient/experiment.rb +33 -36
- data/benchmark/factorial_hash.rb +7 -8
- data/benchmark/factorial_method.rb +4 -6
- data/benchmark/odd.rb +6 -7
- data/benchmark/power.rb +11 -11
- data/bin/distribution +26 -26
- data/data/template/spec.erb +7 -6
- data/distribution.gemspec +25 -0
- data/lib/distribution.rb +79 -124
- data/lib/distribution/beta.rb +6 -8
- data/lib/distribution/beta/gsl.rb +14 -9
- data/lib/distribution/beta/java.rb +1 -1
- data/lib/distribution/beta/ruby.rb +41 -7
- data/lib/distribution/binomial.rb +10 -11
- data/lib/distribution/binomial/gsl.rb +6 -5
- data/lib/distribution/binomial/java.rb +1 -1
- data/lib/distribution/binomial/ruby.rb +22 -15
- data/lib/distribution/bivariatenormal.rb +4 -5
- data/lib/distribution/bivariatenormal/gsl.rb +2 -2
- data/lib/distribution/bivariatenormal/java.rb +1 -1
- data/lib/distribution/bivariatenormal/ruby.rb +245 -254
- data/lib/distribution/chisquare.rb +8 -10
- data/lib/distribution/chisquare/gsl.rb +24 -19
- data/lib/distribution/chisquare/java.rb +1 -1
- data/lib/distribution/chisquare/ruby.rb +60 -55
- data/lib/distribution/chisquare/statistics2.rb +16 -13
- data/lib/distribution/distributable.rb +40 -0
- data/lib/distribution/exponential.rb +4 -5
- data/lib/distribution/exponential/gsl.rb +13 -9
- data/lib/distribution/exponential/ruby.rb +17 -11
- data/lib/distribution/f.rb +10 -11
- data/lib/distribution/f/gsl.rb +26 -22
- data/lib/distribution/f/java.rb +1 -1
- data/lib/distribution/f/ruby.rb +104 -105
- data/lib/distribution/f/statistics2.rb +22 -19
- data/lib/distribution/gamma.rb +5 -7
- data/lib/distribution/gamma/gsl.rb +13 -9
- data/lib/distribution/gamma/java.rb +1 -1
- data/lib/distribution/gamma/ruby.rb +5 -11
- data/lib/distribution/hypergeometric.rb +5 -8
- data/lib/distribution/hypergeometric/gsl.rb +5 -6
- data/lib/distribution/hypergeometric/java.rb +1 -1
- data/lib/distribution/hypergeometric/ruby.rb +34 -35
- data/lib/distribution/logistic.rb +6 -9
- data/lib/distribution/logistic/ruby.rb +14 -9
- data/lib/distribution/lognormal.rb +37 -0
- data/lib/distribution/lognormal/gsl.rb +21 -0
- data/lib/distribution/lognormal/ruby.rb +16 -0
- data/lib/distribution/math_extension.rb +187 -231
- data/lib/distribution/math_extension/chebyshev_series.rb +281 -272
- data/lib/distribution/math_extension/erfc.rb +28 -31
- data/lib/distribution/math_extension/exponential_integral.rb +17 -17
- data/lib/distribution/math_extension/gammastar.rb +19 -20
- data/lib/distribution/math_extension/gsl_utilities.rb +12 -12
- data/lib/distribution/math_extension/incomplete_beta.rb +52 -61
- data/lib/distribution/math_extension/incomplete_gamma.rb +166 -168
- data/lib/distribution/math_extension/log_utilities.rb +20 -22
- data/lib/distribution/normal.rb +11 -13
- data/lib/distribution/normal/gsl.rb +13 -10
- data/lib/distribution/normal/java.rb +30 -1
- data/lib/distribution/normal/ruby.rb +69 -59
- data/lib/distribution/normal/statistics2.rb +5 -2
- data/lib/distribution/normalmultivariate.rb +64 -64
- data/lib/distribution/poisson.rb +12 -14
- data/lib/distribution/poisson/gsl.rb +7 -7
- data/lib/distribution/poisson/java.rb +26 -0
- data/lib/distribution/poisson/ruby.rb +38 -9
- data/lib/distribution/shorthand.rb +17 -0
- data/lib/distribution/t.rb +16 -16
- data/lib/distribution/t/gsl.rb +27 -24
- data/lib/distribution/t/java.rb +1 -1
- data/lib/distribution/t/ruby.rb +99 -100
- data/lib/distribution/t/statistics2.rb +19 -19
- data/lib/distribution/uniform.rb +26 -0
- data/lib/distribution/uniform/gsl.rb +36 -0
- data/lib/distribution/uniform/ruby.rb +91 -0
- data/lib/distribution/version.rb +3 -0
- data/lib/distribution/weibull.rb +10 -0
- data/lib/distribution/weibull/gsl.rb +21 -0
- data/lib/distribution/weibull/ruby.rb +34 -0
- data/spec/beta_spec.rb +48 -50
- data/spec/binomial_spec.rb +80 -84
- data/spec/bivariatenormal_spec.rb +28 -35
- data/spec/chisquare_spec.rb +49 -52
- data/spec/distribution_spec.rb +11 -11
- data/spec/exponential_spec.rb +48 -39
- data/spec/f_spec.rb +73 -71
- data/spec/gamma_spec.rb +50 -53
- data/spec/hypergeometric_spec.rb +63 -69
- data/spec/logistic_spec.rb +31 -37
- data/spec/lognormal_spec.rb +54 -0
- data/spec/math_extension_spec.rb +192 -209
- data/spec/normal_spec.rb +80 -73
- data/spec/poisson_spec.rb +78 -36
- data/spec/shorthand_spec.rb +19 -22
- data/spec/spec_helper.rb +31 -6
- data/spec/t_spec.rb +63 -77
- data/spec/uniform_spec.rb +154 -0
- data/spec/weibull_spec.rb +17 -0
- data/vendor/java/commons-math-2.2.jar +0 -0
- metadata +91 -111
- data.tar.gz.sig +0 -0
- data/.autotest +0 -23
- data/.gemtest +0 -0
- data/Manifest.txt +0 -95
- data/README.txt +0 -100
- metadata.gz.sig +0 -0
@@ -2,38 +2,35 @@ require 'distribution/hypergeometric/ruby'
|
|
2
2
|
require 'distribution/hypergeometric/gsl'
|
3
3
|
require 'distribution/hypergeometric/java'
|
4
4
|
|
5
|
-
|
6
5
|
module Distribution
|
7
6
|
# From Wikipedia:
|
8
7
|
# In probability theory and statistics, the hypergeometric distribution is a discrete probability distribution that
|
9
8
|
# describes the number of successes in a sequence of n draws from a finite population without replacement, just as
|
10
9
|
# the binomial distribution describes the number of successes for draws with replacement.
|
11
10
|
module Hypergeometric
|
12
|
-
SHORTHAND='hypg'
|
11
|
+
SHORTHAND = 'hypg'
|
13
12
|
extend Distributable
|
14
13
|
|
15
14
|
create_distribution_methods
|
16
15
|
|
17
16
|
##
|
18
17
|
# :singleton-method: pdf(k,m,n,total)
|
19
|
-
# This function computes the probability p(k) of obtaining k
|
20
|
-
# from a hypergeometric distribution with parameters
|
18
|
+
# This function computes the probability p(k) of obtaining k
|
19
|
+
# from a hypergeometric distribution with parameters
|
21
20
|
# m, n t.
|
22
21
|
# * m: number of elements with desired attribute on population
|
23
|
-
# * n: sample size
|
22
|
+
# * n: sample size
|
24
23
|
# * t: population size
|
25
24
|
|
26
25
|
##
|
27
26
|
# :singleton-method: cdf(k,m,n,total)
|
28
27
|
# These functions compute the cumulative distribution function P(k)
|
29
|
-
# for the hypergeometric distribution with parameters m, n and t.
|
28
|
+
# for the hypergeometric distribution with parameters m, n and t.
|
30
29
|
# * m: number of elements with desired attribute on population
|
31
30
|
# * n: sample size
|
32
31
|
# * t: population size
|
33
|
-
|
34
32
|
|
35
33
|
##
|
36
34
|
# :singleton-method: p_value(k,m,n,total)
|
37
|
-
|
38
35
|
end
|
39
36
|
end
|
@@ -3,15 +3,14 @@ module Distribution
|
|
3
3
|
module GSL_
|
4
4
|
class << self
|
5
5
|
def pdf(k, m, n, total) # :nodoc:
|
6
|
-
GSL::Ran
|
6
|
+
GSL::Ran.hypergeometric_pdf(k, m, total - m, n)
|
7
7
|
end
|
8
|
-
# The GSL::Cdf function for hypergeometric
|
9
|
-
#
|
8
|
+
# The GSL::Cdf function for hypergeometric
|
9
|
+
#
|
10
10
|
def cdf(k, m, n, total) # :nodoc:
|
11
|
-
GSL::Cdf
|
11
|
+
GSL::Cdf.hypergeometric_P(k, m, total - m, n)
|
12
12
|
end
|
13
|
-
|
14
13
|
end
|
15
14
|
end
|
16
15
|
end
|
17
|
-
end
|
16
|
+
end
|
@@ -1,13 +1,14 @@
|
|
1
|
-
# Added by John O. Woods, SciRuby project.
|
1
|
+
# Added by John O. Woods, SciRuby project.
|
2
2
|
# Optimized by Claudio Bustos
|
3
3
|
|
4
4
|
module Distribution
|
5
5
|
module Hypergeometric
|
6
6
|
module Ruby_
|
7
7
|
class << self
|
8
|
-
def bc(n,k)
|
9
|
-
Math.binomial_coefficient(n,k)
|
8
|
+
def bc(n, k)
|
9
|
+
Math.binomial_coefficient(n, k)
|
10
10
|
end
|
11
|
+
|
11
12
|
# Hypergeometric probability density function
|
12
13
|
#
|
13
14
|
# Probability p(+k+, +m+, +n+, +total+) of drawing sets of size +m+ and +n+ with an intersection of size +k+
|
@@ -17,50 +18,48 @@ module Distribution
|
|
17
18
|
# * http://www.gnu.org/software/gsl/manual/html_node/The-Hypergeometric-Distribution.html
|
18
19
|
# * http://en.wikipedia.org/wiki/Hypergeometric_distribution
|
19
20
|
def pdf(k, m, n, total)
|
20
|
-
min_m_n=m<n ? m : n
|
21
|
-
max_t=[0,m+n-total].max
|
22
|
-
return 0 if k>min_m_n
|
23
|
-
(bc(m,k) * bc(total-m,n-k)).quo(bc(total,n))
|
24
|
-
end
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def pdf_with_den(k,m,n,total,den)
|
29
|
-
(bc(m,k) * bc(total-m,n-k)).quo(den)
|
21
|
+
min_m_n = m < n ? m : n
|
22
|
+
max_t = [0, m + n - total].max
|
23
|
+
return 0 if k > min_m_n || k < max_t
|
24
|
+
(bc(m, k) * bc(total - m, n - k)).quo(bc(total, n))
|
30
25
|
end
|
31
26
|
|
32
|
-
|
33
|
-
|
34
|
-
def
|
35
|
-
|
36
|
-
den=bc(total,n)
|
37
|
-
|
38
|
-
(0..total).each do |i|
|
39
|
-
ac+=pdf_with_den(i,m,n,total,den)
|
40
|
-
return i if ac>=pr
|
41
|
-
end
|
27
|
+
alias_method :exact_pdf, :pdf
|
28
|
+
|
29
|
+
def pdf_with_den(k, m, n, total, den)
|
30
|
+
(bc(m, k) * bc(total - m, n - k)).quo(den)
|
42
31
|
end
|
32
|
+
|
43
33
|
# Cumulative distribution function.
|
44
34
|
# The probability of obtain, from a sample of
|
45
35
|
# size +n+, +k+ or less elements
|
46
36
|
# in a population size +total+ with +m+ interesting elements.
|
47
|
-
#
|
37
|
+
#
|
48
38
|
# Slow, but secure
|
49
39
|
def cdf(k, m, n, total)
|
50
|
-
|
51
|
-
|
40
|
+
fail(ArgumentError, 'k>m') if k > m
|
41
|
+
fail(ArgumentError, 'k>n') if k > n
|
52
42
|
# Store the den
|
53
|
-
den=bc(total,n)
|
54
|
-
(0..k).collect { |ki| pdf_with_den(ki,m,n,total,den) }.inject { |sum,v| sum+v}
|
43
|
+
den = bc(total, n)
|
44
|
+
(0..k).collect { |ki| pdf_with_den(ki, m, n, total, den) }.inject { |sum, v| sum + v }
|
45
|
+
end
|
46
|
+
|
47
|
+
alias_method :exact_cdf, :cdf
|
48
|
+
|
49
|
+
# p-value:
|
50
|
+
def quantile(pr, m, n, total)
|
51
|
+
ac = 0
|
52
|
+
den = bc(total, n)
|
53
|
+
|
54
|
+
(0..total).each do |i|
|
55
|
+
ac += pdf_with_den(i, m, n, total, den)
|
56
|
+
return i if ac >= pr
|
57
|
+
end
|
55
58
|
end
|
56
|
-
|
57
|
-
|
58
|
-
alias :exact_pdf :pdf
|
59
|
-
alias :exact_p_value :p_value
|
60
|
-
alias :exact_cdf :cdf
|
61
59
|
|
62
|
-
|
60
|
+
alias_method :p_value, :quantile
|
61
|
+
alias_method :exact_p_value, :p_value
|
63
62
|
end
|
64
63
|
end
|
65
64
|
end
|
66
|
-
end
|
65
|
+
end
|
@@ -1,13 +1,12 @@
|
|
1
1
|
require 'distribution/logistic/ruby'
|
2
|
-
#require 'distribution/logistic/gsl'
|
3
|
-
#require 'distribution/logistic/java'
|
4
|
-
|
2
|
+
# require 'distribution/logistic/gsl'
|
3
|
+
# require 'distribution/logistic/java'
|
5
4
|
|
6
5
|
module Distribution
|
7
6
|
# From Wikipedia:
|
8
7
|
# In probability theory and statistics, the logistic distribution is a continuous probability distribution. Its cumulative distribution function is the logistic function, which appears in logistic regression and feedforward neural networks. It resembles the normal distribution in shape but has heavier tails (higher kurtosis).
|
9
8
|
module Logistic
|
10
|
-
SHORTHAND='logis'
|
9
|
+
SHORTHAND = 'logis'
|
11
10
|
extend Distributable
|
12
11
|
create_distribution_methods
|
13
12
|
##
|
@@ -21,20 +20,18 @@ module Distribution
|
|
21
20
|
# Returns the pdf for logistic distribution (f(x,u,s))
|
22
21
|
# * u: mean
|
23
22
|
# * s: variance related parameter
|
24
|
-
|
23
|
+
|
25
24
|
##
|
26
|
-
# :singleton-method: cdf(x
|
25
|
+
# :singleton-method: cdf(x,u,s)
|
27
26
|
# Returns the cdf for logistic distribution (F(x,u,s))
|
28
27
|
# * u: mean
|
29
28
|
# * s: variance related parameter
|
30
29
|
|
31
30
|
##
|
32
31
|
# :singleton-method: p_value(pr , u,s)
|
33
|
-
# Returns the inverse cdf for logistic distribution
|
32
|
+
# Returns the inverse cdf for logistic distribution
|
34
33
|
# (F^-1(pr,u,s))
|
35
34
|
# * u: mean
|
36
35
|
# * s: variance related parameter
|
37
|
-
|
38
|
-
|
39
36
|
end
|
40
37
|
end
|
@@ -2,18 +2,23 @@ module Distribution
|
|
2
2
|
module Logistic
|
3
3
|
module Ruby_
|
4
4
|
class << self
|
5
|
-
|
6
|
-
|
7
|
-
end
|
8
|
-
def pdf(x,u,s )
|
9
|
-
(Math.exp(-(x-u)/s))/(s*(1+Math.exp(-(x-u)/s)**2))
|
5
|
+
def rng(u, s)
|
6
|
+
-> { p_value(rand, u, s) }
|
10
7
|
end
|
11
|
-
|
12
|
-
|
8
|
+
|
9
|
+
def pdf(x, u, s)
|
10
|
+
(Math.exp(-(x - u) / s)) / (s * (1 + Math.exp(-(x - u) / s)**2))
|
13
11
|
end
|
14
|
-
|
15
|
-
|
12
|
+
|
13
|
+
def cdf(x, u, s)
|
14
|
+
1 / (1 + Math.exp(-(x - u) / s))
|
16
15
|
end
|
16
|
+
|
17
|
+
def quantile(pr, u, s)
|
18
|
+
u + s * Math.log(pr / (1 - pr))
|
19
|
+
end
|
20
|
+
|
21
|
+
alias_method :p_value, :quantile
|
17
22
|
end
|
18
23
|
end
|
19
24
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'distribution/lognormal/gsl'
|
2
|
+
require 'distribution/lognormal/ruby'
|
3
|
+
|
4
|
+
module Distribution
|
5
|
+
# From Wikipedia:
|
6
|
+
# In probability theory, a log-normal distribution is a probability
|
7
|
+
# distribution of a random variable whose logarithm is normally
|
8
|
+
# distributed. If X is a random variable with a normal distribution, then
|
9
|
+
# Y = exp(X) has a log-normal distribution; likewise, if Y is
|
10
|
+
# log-normally distributed, then X = log(Y) is normally distributed. (This
|
11
|
+
# is true regardless of the base of the logarithmic function: if loga(Y) is
|
12
|
+
# normally distributed, then so is logb(Y), for any two positive numbers
|
13
|
+
# a, b ≠ 1.)
|
14
|
+
#
|
15
|
+
# This module calculates the pdf, cdf and inverse cdf for the Lognormal distribution.
|
16
|
+
#
|
17
|
+
module LogNormal
|
18
|
+
extend Distributable
|
19
|
+
SHORTHAND = 'lognormal'
|
20
|
+
create_distribution_methods
|
21
|
+
|
22
|
+
##
|
23
|
+
# :singleton-method: pdf(x,u,s)
|
24
|
+
# Returns the PDF of the Lognormal distribution of x with parameters u (position) and
|
25
|
+
# s (deviation)
|
26
|
+
|
27
|
+
##
|
28
|
+
# :singleton-method: cdf(x,u,s)
|
29
|
+
# Returns the CDF of the Lognormal distribution of x with parameters u (position) and
|
30
|
+
# s(deviation)
|
31
|
+
|
32
|
+
##
|
33
|
+
# :singleton-method: p_value(pr,u,s)
|
34
|
+
# Return the quantile of the corresponding integral +pr+
|
35
|
+
# on a lognormal distribution's cdf with parameters z and s
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Distribution
|
2
|
+
module LogNormal
|
3
|
+
module GSL_
|
4
|
+
class << self
|
5
|
+
def pdf(x, u, s)
|
6
|
+
GSL::Ran.lognormal_pdf(x.to_f, u.to_f, s.to_f)
|
7
|
+
end
|
8
|
+
|
9
|
+
def cdf(x, u, s)
|
10
|
+
GSL::Cdf.lognormal_P(x.to_f, u.to_f, s.to_f)
|
11
|
+
end
|
12
|
+
|
13
|
+
def quantile(pr, u, s)
|
14
|
+
GSL::Cdf.lognormal_Pinv(pr.to_f, u.to_f, s.to_f)
|
15
|
+
end
|
16
|
+
|
17
|
+
alias_method :p_value, :quantile
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Distribution
|
2
|
+
module LogNormal
|
3
|
+
module Ruby_
|
4
|
+
class << self
|
5
|
+
def pdf(x, u, s)
|
6
|
+
fail 'x should be > 0 ' if x < 0
|
7
|
+
(1.0 / (x * s * Math.sqrt(2 * Math::PI))) * Math.exp(-((Math.log(x) - u)**2 / (2 * s**2)))
|
8
|
+
end
|
9
|
+
|
10
|
+
def cdf(x, u, s)
|
11
|
+
Distribution::Normal.cdf((Math.log(x) - u) / s)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,78 +1,19 @@
|
|
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'
|
28
4
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
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'
|
66
15
|
|
67
|
-
|
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
|
-
end
|
16
|
+
module Distribution
|
76
17
|
# Useful additions to Math
|
77
18
|
module MathExtension
|
78
19
|
# Factorization based on Prime Swing algorithm, by Luschny (the king of factorial numbers analysis :P )
|
@@ -80,200 +21,221 @@ module Distribution
|
|
80
21
|
# * The Homepage of Factorial Algorithms. (C) Peter Luschny, 2000-2010
|
81
22
|
# == URL: http://www.luschny.de/math/factorial/csharp/FactorialPrimeSwing.cs.html
|
82
23
|
class SwingFactorial
|
24
|
+
SmallOddSwing = [1, 1, 1, 3, 3, 15, 5, 35, 35, 315, 63, 693, 231, 3003,
|
25
|
+
429, 6435, 6435, 109_395, 12_155, 230_945, 46_189,
|
26
|
+
969_969, 88_179, 2_028_117, 676_039, 16_900_975,
|
27
|
+
1_300_075, 35_102_025, 5_014_575, 145_422_675,
|
28
|
+
9_694_845, 300_540_195, 300_540_195]
|
29
|
+
|
30
|
+
SmallFactorial = [1, 1, 2, 6, 24, 120, 720, 5040, 40_320, 362_880,
|
31
|
+
3_628_800, 39_916_800, 479_001_600, 6_227_020_800,
|
32
|
+
87_178_291_200, 1_307_674_368_000, 20_922_789_888_000,
|
33
|
+
355_687_428_096_000, 6_402_373_705_728_000,
|
34
|
+
121_645_100_408_832_000, 2_432_902_008_176_640_000]
|
35
|
+
|
83
36
|
attr_reader :result
|
84
|
-
|
85
|
-
SmallFactorial=[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000, 355687428096000, 6402373705728000, 121645100408832000, 2432902008176640000]
|
37
|
+
|
86
38
|
def bitcount(n)
|
87
|
-
bc = n - ((n >> 1) & 0x55555555)
|
88
|
-
bc = (bc & 0x33333333) + ((bc >> 2) & 0x33333333)
|
89
|
-
bc = (bc + (bc >> 4)) & 0x0f0f0f0f
|
90
|
-
bc += bc >> 8
|
91
|
-
bc += bc >> 16
|
92
|
-
bc
|
39
|
+
bc = n - ((n >> 1) & 0x55555555)
|
40
|
+
bc = (bc & 0x33333333) + ((bc >> 2) & 0x33333333)
|
41
|
+
bc = (bc + (bc >> 4)) & 0x0f0f0f0f
|
42
|
+
bc += bc >> 8
|
43
|
+
bc += bc >> 16
|
44
|
+
bc &= 0x3f
|
93
45
|
bc
|
94
46
|
end
|
47
|
+
|
95
48
|
def initialize(n)
|
96
|
-
if
|
97
|
-
@result=SmallFactorial[n]
|
98
|
-
#naive_factorial(n)
|
49
|
+
if n < 20
|
50
|
+
@result = SmallFactorial[n]
|
51
|
+
# naive_factorial(n)
|
99
52
|
else
|
100
|
-
|
101
|
-
|
102
|
-
|
53
|
+
@prime_list = []
|
54
|
+
exp2 = n - bitcount(n)
|
55
|
+
@result = recfactorial(n) << exp2
|
103
56
|
end
|
104
57
|
end
|
58
|
+
|
105
59
|
def recfactorial(n)
|
106
|
-
return 1 if n<2
|
107
|
-
(recfactorial(n/2)**2) * swing(n)
|
60
|
+
return 1 if n < 2
|
61
|
+
(recfactorial(n / 2)**2) * swing(n)
|
108
62
|
end
|
63
|
+
|
109
64
|
def swing(n)
|
110
|
-
return SmallOddSwing[n] if
|
65
|
+
return SmallOddSwing[n] if n < 33
|
111
66
|
sqrtN = Math.sqrt(n).floor
|
112
|
-
count=0
|
113
|
-
|
114
|
-
Prime.each(n/3) do |prime|
|
115
|
-
next if prime<3
|
116
|
-
if (prime<=sqrtN)
|
117
|
-
q=n
|
118
|
-
_p=1
|
119
|
-
|
120
|
-
while(
|
121
|
-
if
|
122
|
-
_p*=prime
|
123
|
-
end
|
67
|
+
count = 0
|
68
|
+
|
69
|
+
Prime.each(n / 3) do |prime|
|
70
|
+
next if prime < 3
|
71
|
+
if (prime <= sqrtN)
|
72
|
+
q = n
|
73
|
+
_p = 1
|
74
|
+
|
75
|
+
while (q = (q / prime).truncate) > 0
|
76
|
+
_p *= prime if q.odd?
|
124
77
|
end
|
125
|
-
if _p>1
|
126
|
-
@prime_list[count]=_p
|
127
|
-
count+=1
|
78
|
+
if _p > 1
|
79
|
+
@prime_list[count] = _p
|
80
|
+
count += 1
|
128
81
|
end
|
129
|
-
|
82
|
+
|
130
83
|
else
|
131
|
-
if (
|
132
|
-
@prime_list[count]=prime
|
133
|
-
count+=1
|
84
|
+
if (n / prime).truncate.odd?
|
85
|
+
@prime_list[count] = prime
|
86
|
+
count += 1
|
134
87
|
end
|
135
88
|
end
|
136
89
|
end
|
137
|
-
prod=get_primorial((n/2).truncate+1,n)
|
138
|
-
prod * @prime_list[0,count].inject(1) {|ac,v| ac*v}
|
90
|
+
prod = get_primorial((n / 2).truncate + 1, n)
|
91
|
+
prod * @prime_list[0, count].inject(1) { |ac, v| ac * v }
|
139
92
|
end
|
140
|
-
|
141
|
-
|
93
|
+
|
94
|
+
def get_primorial(low, up)
|
95
|
+
prod = 1
|
142
96
|
Prime.each(up) do |prime|
|
143
|
-
next if prime<low
|
144
|
-
prod*=prime
|
97
|
+
next if prime < low
|
98
|
+
prod *= prime
|
145
99
|
end
|
146
100
|
prod
|
147
101
|
end
|
102
|
+
|
148
103
|
def naive_factorial(n)
|
149
|
-
@result=(self.class).naive_factorial(n)
|
104
|
+
@result = (self.class).naive_factorial(n)
|
150
105
|
end
|
106
|
+
|
151
107
|
def self.naive_factorial(n)
|
152
|
-
(2..n).inject(1) { |f,nn| f * nn }
|
108
|
+
(2..n).inject(1) { |f, nn| f * nn }
|
153
109
|
end
|
154
110
|
end
|
111
|
+
|
155
112
|
# Module to calculate approximated factorial
|
156
113
|
# Based (again) on Luschny formula, with 16 digits of precision
|
157
114
|
# == Reference
|
158
115
|
# * http://www.luschny.de/math/factorial/approx/SimpleCases.html
|
159
116
|
module ApproxFactorial
|
160
|
-
|
161
|
-
|
162
|
-
|
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
|
-
|
117
|
+
class << self
|
118
|
+
def stieltjes_ln_factorial(z)
|
119
|
+
a0 = 1.quo(12); a1 = 1.quo(30); a2 = 53.quo(210); a3 = 195.quo(371)
|
120
|
+
a4 = 22_999.quo(22_737); a5 = 29_944_523.quo(19_733_142)
|
121
|
+
a6 = 109_535_241_009.quo(48_264_275_462)
|
122
|
+
zz = z + 1
|
123
|
+
|
124
|
+
(1.quo(2)) * Math.log(2 * Math::PI) + (zz - 1.quo(2)) * Math.log(zz) - zz +
|
125
|
+
a0.quo(zz + a1.quo(zz + a2.quo(zz + a3.quo(zz + a4.quo(zz + a5.quo(zz + a6.quo(zz)))))))
|
126
|
+
end
|
127
|
+
|
128
|
+
def stieltjes_ln_factorial_big(z)
|
129
|
+
a0 = 1 / 12.0; a1 = 1 / 30.0; a2 = 53 / 210.0; a3 = 195 / 371.0
|
130
|
+
a4 = 22_999 / 22_737.0; a5 = 29_944_523 / 19_733_142.0
|
131
|
+
a6 = 109_535_241_009 / 48_264_275_462.0
|
132
|
+
zz = z + 1
|
133
|
+
|
134
|
+
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((
|
135
|
+
a0 / (zz + a1 / (zz + a2 / (zz + a3 / (zz + a4 / (zz + a5 / (zz + a6 / zz))))))
|
136
|
+
).to_s)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Valid upto 11 digits
|
140
|
+
def stieltjes_factorial(x)
|
141
|
+
y = x
|
142
|
+
_p = 1
|
143
|
+
|
144
|
+
_p *= y; y += 1 while y < 8
|
145
|
+
|
146
|
+
lr = stieltjes_ln_factorial(y)
|
147
|
+
r = Math.exp(lr)
|
148
|
+
|
149
|
+
if r.infinite?
|
150
|
+
r = BigMath.exp(BigDecimal(lr.to_s), 20)
|
151
|
+
r = (r * x) / (_p * y) if x < 8
|
152
|
+
r = r.to_i
|
153
|
+
else
|
154
|
+
r = (r * x) / (_p * y) if x < 8
|
188
155
|
end
|
189
|
-
|
190
|
-
r = Math.exp(lr)
|
191
|
-
#puts "valid: #{5/2.0+(13/2.0)*Math::log(x)}"
|
192
|
-
if r.infinite?
|
193
|
-
r=BigMath.exp(BigDecimal(lr.to_s),20)
|
194
|
-
r = (r*x) / (_p*y) if x < 8
|
195
|
-
r=r.to_i
|
196
|
-
else
|
197
|
-
r = (r*x) / (_p*y) if x < 8
|
156
|
+
r
|
198
157
|
end
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Exact factorial.
|
162
|
+
# Use lookup on a Hash table on n<20
|
163
|
+
# and Prime Swing algorithm for higher values.
|
205
164
|
def factorial(n)
|
206
165
|
SwingFactorial.new(n).result
|
207
166
|
end
|
167
|
+
|
208
168
|
# Approximate factorial, up to 16 digits
|
209
169
|
# Based of Luschy algorithm
|
210
170
|
def fast_factorial(n)
|
211
171
|
ApproxFactorial.stieltjes_factorial(n)
|
212
172
|
end
|
213
|
-
|
173
|
+
|
214
174
|
# Beta function.
|
215
175
|
# Source:
|
216
176
|
# * http://mathworld.wolfram.com/BetaFunction.html
|
217
|
-
def beta(x,y)
|
218
|
-
(gamma(x)*gamma(y)).quo(gamma(x+y))
|
177
|
+
def beta(x, y)
|
178
|
+
(gamma(x) * gamma(y)).quo(gamma(x + y))
|
219
179
|
end
|
220
180
|
|
221
181
|
# Get pure-Ruby logbeta
|
222
|
-
def logbeta(x,y)
|
223
|
-
Beta.log_beta(x,y).first
|
182
|
+
def logbeta(x, y)
|
183
|
+
Beta.log_beta(x, y).first
|
224
184
|
end
|
225
185
|
|
226
186
|
# Log beta function conforming to style of lgamma (returns sign in second array index)
|
227
|
-
def lbeta(x,y)
|
228
|
-
Beta.log_beta(x,y)
|
187
|
+
def lbeta(x, y)
|
188
|
+
Beta.log_beta(x, y)
|
229
189
|
end
|
230
190
|
|
231
191
|
# I_x(a,b): Regularized incomplete beta function
|
232
192
|
# Fast version. For a exact calculation, based on factorial
|
233
193
|
# use exact_regularized_beta_function
|
234
|
-
def regularized_beta(x,a,b)
|
235
|
-
return 1 if x==1
|
236
|
-
IncompleteBeta.evaluate(a,b,x)
|
194
|
+
def regularized_beta(x, a, b)
|
195
|
+
return 1 if x == 1
|
196
|
+
IncompleteBeta.evaluate(a, b, x)
|
237
197
|
end
|
198
|
+
|
238
199
|
# I_x(a,b): Regularized incomplete beta function
|
239
200
|
# TODO: Find a faster version.
|
240
201
|
# Source:
|
241
202
|
# * http://dlmf.nist.gov/8.17
|
242
|
-
def exact_regularized_beta(x,a,b)
|
243
|
-
return 1 if x==1
|
244
|
-
|
245
|
-
|
246
|
-
(
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
203
|
+
def exact_regularized_beta(x, a, b)
|
204
|
+
return 1 if x == 1
|
205
|
+
|
206
|
+
m = a.to_i
|
207
|
+
n = (b + a - 1).to_i
|
208
|
+
|
209
|
+
(m..n).inject(0) do|sum, j|
|
210
|
+
sum + (binomial_coefficient(n, j) * x**j * (1 - x)**(n - j))
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
252
214
|
# Incomplete beta function: B(x;a,b)
|
253
|
-
# +a+ and +b+ are parameters and +x+ is
|
215
|
+
# +a+ and +b+ are parameters and +x+ is
|
254
216
|
# integration upper limit.
|
255
|
-
def incomplete_beta(x,a,b)
|
256
|
-
IncompleteBeta.evaluate(a,b,x)*beta(a,b)
|
257
|
-
#Math::IncompleteBeta.axpy(1.0, 0.0, a,b,x)
|
217
|
+
def incomplete_beta(x, a, b)
|
218
|
+
IncompleteBeta.evaluate(a, b, x) * beta(a, b)
|
219
|
+
# Math::IncompleteBeta.axpy(1.0, 0.0, a,b,x)
|
258
220
|
end
|
259
|
-
|
221
|
+
|
260
222
|
# Rising factorial
|
261
|
-
def rising_factorial(x,n)
|
262
|
-
factorial(x+n-1).quo(factorial(x-1))
|
223
|
+
def rising_factorial(x, n)
|
224
|
+
factorial(x + n - 1).quo(factorial(x - 1))
|
263
225
|
end
|
264
|
-
|
226
|
+
|
265
227
|
# Ln of gamma
|
266
228
|
def loggamma(x)
|
267
229
|
Math.lgamma(x).first
|
268
230
|
end
|
269
231
|
|
270
232
|
def incomplete_gamma(a, x = 0, with_error = false)
|
271
|
-
IncompleteGamma.p(a,x, with_error)
|
233
|
+
IncompleteGamma.p(a, x, with_error)
|
272
234
|
end
|
273
|
-
|
235
|
+
alias_method :gammp, :incomplete_gamma
|
274
236
|
|
275
237
|
def gammq(a, x, with_error = false)
|
276
|
-
IncompleteGamma.q(a,x,with_error)
|
238
|
+
IncompleteGamma.q(a, x, with_error)
|
277
239
|
end
|
278
240
|
|
279
241
|
def unnormalized_incomplete_gamma(a, x, with_error = false)
|
@@ -281,36 +243,36 @@ module Distribution
|
|
281
243
|
end
|
282
244
|
|
283
245
|
# Not the same as erfc. This is the GSL version, which may have slightly different results.
|
284
|
-
def erfc_e
|
246
|
+
def erfc_e(x, with_error = false)
|
285
247
|
Erfc.evaluate(x, with_error)
|
286
248
|
end
|
287
|
-
|
249
|
+
|
288
250
|
# Sequences without repetition. n^k'
|
289
251
|
# Also called 'failing factorial'
|
290
|
-
def permutations(n,k)
|
291
|
-
return 1 if k==0
|
292
|
-
return n if k==1
|
293
|
-
return factorial(n) if k==n
|
294
|
-
(((n-k+1)..n).inject(1) {|ac,v| ac * v})
|
295
|
-
#factorial(x).quo(factorial(x-n))
|
252
|
+
def permutations(n, k)
|
253
|
+
return 1 if k == 0
|
254
|
+
return n if k == 1
|
255
|
+
return factorial(n) if k == n
|
256
|
+
(((n - k + 1)..n).inject(1) { |ac, v| ac * v })
|
257
|
+
# factorial(x).quo(factorial(x-n))
|
296
258
|
end
|
297
|
-
|
259
|
+
|
298
260
|
# Binomial coeffients, or:
|
299
261
|
# ( n )
|
300
262
|
# ( k )
|
301
263
|
#
|
302
264
|
# Gives the number of *different* k size subsets of a set size n
|
303
|
-
#
|
265
|
+
#
|
304
266
|
# Uses:
|
305
267
|
#
|
306
268
|
# (n) n^k' (n)..(n-k+1)
|
307
269
|
# ( ) = ---- = ------------
|
308
270
|
# (k) k! k!
|
309
271
|
#
|
310
|
-
def binomial_coefficient(n,k)
|
311
|
-
return 1 if
|
312
|
-
k=[k, n-k].min
|
313
|
-
permutations(n,k).quo(factorial(k))
|
272
|
+
def binomial_coefficient(n, k)
|
273
|
+
return 1 if k == 0 || k == n
|
274
|
+
k = [k, n - k].min
|
275
|
+
permutations(n, k).quo(factorial(k))
|
314
276
|
# The factorial way is
|
315
277
|
# factorial(n).quo(factorial(k)*(factorial(n-k)))
|
316
278
|
# The multiplicative way is
|
@@ -319,54 +281,48 @@ module Distribution
|
|
319
281
|
# Binomial coefficient using multiplicative algorithm
|
320
282
|
# On benchmarks, is faster that raising factorial method
|
321
283
|
# when k is little. Use only when you're sure of that.
|
322
|
-
def binomial_coefficient_multiplicative(n,k)
|
323
|
-
return 1 if
|
324
|
-
k=[k, n-k].min
|
325
|
-
(1..k).inject(1) {|ac, i| (ac*(n-k+i).quo(i))}
|
284
|
+
def binomial_coefficient_multiplicative(n, k)
|
285
|
+
return 1 if k == 0 || k == n
|
286
|
+
k = [k, n - k].min
|
287
|
+
(1..k).inject(1) { |ac, i| (ac * (n - k + i).quo(i)) }
|
326
288
|
end
|
327
|
-
|
289
|
+
|
328
290
|
# Approximate binomial coefficient, using gamma function.
|
329
291
|
# The fastest method, until we fall on BigDecimal!
|
330
|
-
def binomial_coefficient_gamma(n,k)
|
331
|
-
return 1 if
|
332
|
-
k=[k, n-k].min
|
292
|
+
def binomial_coefficient_gamma(n, k)
|
293
|
+
return 1 if k == 0 || k == n
|
294
|
+
k = [k, n - k].min
|
333
295
|
# First, we try direct gamma calculation for max precission
|
334
296
|
|
335
|
-
val=gamma(n + 1).quo(gamma(k+1)*gamma(n-k+1))
|
297
|
+
val = gamma(n + 1).quo(gamma(k + 1) * gamma(n - k + 1))
|
336
298
|
# Ups. Outside float point range. We try with logs
|
337
|
-
if
|
338
|
-
#puts "nan"
|
339
|
-
lg=loggamma(
|
340
|
-
val=Math.exp(lg)
|
299
|
+
if val.nan?
|
300
|
+
# puts "nan"
|
301
|
+
lg = loggamma(n + 1) - (loggamma(k + 1) + loggamma(n - k + 1))
|
302
|
+
val = Math.exp(lg)
|
341
303
|
# Crash again! We require BigDecimals
|
342
304
|
if val.infinite?
|
343
|
-
#puts "infinite"
|
344
|
-
val=BigMath.exp(BigDecimal(lg.to_s),16)
|
305
|
+
# puts "infinite"
|
306
|
+
val = BigMath.exp(BigDecimal(lg.to_s), 16)
|
345
307
|
end
|
346
308
|
end
|
347
309
|
val
|
348
310
|
end
|
349
|
-
|
311
|
+
alias_method :combinations, :binomial_coefficient
|
350
312
|
end
|
351
313
|
end
|
352
314
|
|
353
315
|
module Math
|
354
316
|
include Distribution::MathExtension
|
355
|
-
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
|
317
|
+
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
|
356
318
|
end
|
357
319
|
|
358
320
|
# Necessary on Ruby 1.9
|
359
321
|
module CMath # :nodoc:
|
360
322
|
include Distribution::MathExtension
|
361
|
-
module_function :factorial, :beta, :loggamma, :unnormalized_incomplete_gamma,
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
remove_method :loggamma
|
367
|
-
include Distribution::MathExtension18
|
368
|
-
module_function :gamma, :loggamma
|
369
|
-
end
|
323
|
+
module_function :factorial, :beta, :loggamma, :unnormalized_incomplete_gamma,
|
324
|
+
:incomplete_gamma, :gammp, :gammq, :erfc_e, :binomial_coefficient,
|
325
|
+
:binomial_coefficient_gamma, :incomplete_beta, :exact_regularized_beta,
|
326
|
+
:regularized_beta, :permutations, :rising_factorial, :fast_factorial,
|
327
|
+
:combinations, :logbeta, :lbeta
|
370
328
|
end
|
371
|
-
|
372
|
-
|