simple-random 0.10.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +81 -0
- data/LICENSE +130 -251
- data/README.rdoc +9 -0
- data/Rakefile +37 -16
- data/VERSION +1 -1
- data/lib/simple-random.rb +2 -207
- data/lib/simple-random/multi_threaded_simple_random.rb +30 -0
- data/lib/simple-random/simple_random.rb +207 -0
- data/simple-random.gemspec +32 -12
- data/test/helper.rb +28 -2
- data/test/test_simple_random.rb +118 -5
- metadata +112 -55
data/README.rdoc
CHANGED
@@ -12,6 +12,7 @@ Generate random numbers sampled from the following distributions:
|
|
12
12
|
* Laplace (double exponential)
|
13
13
|
* Normal
|
14
14
|
* Student t
|
15
|
+
* Triangular
|
15
16
|
* Uniform
|
16
17
|
* Weibull
|
17
18
|
|
@@ -33,6 +34,14 @@ Distributed under the Code Project Open License, which is similar to MIT or BSD.
|
|
33
34
|
|
34
35
|
== History
|
35
36
|
|
37
|
+
=== 1.0.0 - 2014-07-08
|
38
|
+
* Migrate to new version of Jeweler for gem packaging
|
39
|
+
* Merge jwroblewski's changes into a new multi-threaded simple random class
|
40
|
+
* Change from Code Project Open License to CDDL-1.0[http://opensource.org/licenses/CDDL-1.0]
|
41
|
+
|
42
|
+
=== 0.10.0 - 2014-03-31
|
43
|
+
* Sample from triangular distribution (thanks to benedictleejh[https://github.com/benedictleejh])
|
44
|
+
|
36
45
|
=== 0.9.3 - 2011-09-16
|
37
46
|
* Sample from Dirichlet distribution with given set of parameters
|
38
47
|
|
data/Rakefile
CHANGED
@@ -1,22 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
2
12
|
require 'rake'
|
3
13
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
16
|
-
Jeweler::GemcutterTasks.new
|
17
|
-
rescue LoadError
|
18
|
-
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
|
17
|
+
gem.name = "simple-random"
|
18
|
+
gem.homepage = "http://github.com/ealdent/simple-random"
|
19
|
+
gem.licenses = "CDDL-1.0"
|
20
|
+
gem.summary = %Q{Simple Random Number Generator}
|
21
|
+
gem.description = %Q{Simple Random Number Generator including Beta, Cauchy, Chi square, Exponential, Gamma, Inverse Gamma, Laplace (double exponential), Normal, Student t, Uniform, and Weibull. Ported from John D. Cook's C# Code.}
|
22
|
+
gem.email = "jasonmadams@gmail.com"
|
23
|
+
gem.authors = ["John D. Cook", "Jason Adams"]
|
24
|
+
# dependencies defined in Gemfile
|
19
25
|
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
20
27
|
|
21
28
|
require 'rake/testtask'
|
22
29
|
Rake::TestTask.new(:test) do |test|
|
@@ -25,6 +32,20 @@ Rake::TestTask.new(:test) do |test|
|
|
25
32
|
test.verbose = true
|
26
33
|
end
|
27
34
|
|
28
|
-
|
35
|
+
desc "Code coverage detail"
|
36
|
+
task :simplecov do
|
37
|
+
ENV['COVERAGE'] = "true"
|
38
|
+
Rake::Task['test'].execute
|
39
|
+
end
|
29
40
|
|
30
41
|
task :default => :test
|
42
|
+
|
43
|
+
require 'rdoc/task'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
46
|
+
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = "simple-random #{version}"
|
49
|
+
rdoc.rdoc_files.include('README*')
|
50
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
data/lib/simple-random.rb
CHANGED
@@ -1,207 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
@m_w = 521288629
|
4
|
-
@m_z = 362436069
|
5
|
-
end
|
6
|
-
|
7
|
-
def set_seed(*args)
|
8
|
-
if args.size > 1
|
9
|
-
@m_w = args.first.to_i if args.first.to_i != 0
|
10
|
-
@m_z = args.last.to_i if args.last.to_i != 0
|
11
|
-
elsif args.first.is_a?(Numeric)
|
12
|
-
@m_w = args.first.to_i if args.first.to_i != 0
|
13
|
-
elsif args.first.is_a?(Time)
|
14
|
-
x = (args.first.to_f * 1000000).to_i
|
15
|
-
@m_w = x >> 16
|
16
|
-
@m_z = x % 4294967296 # 2 ** 32
|
17
|
-
else
|
18
|
-
x = (Time.now.to_f * 1000000).to_i
|
19
|
-
@m_w = x >> 16
|
20
|
-
@m_z = x % 4294967296 # 2 ** 32
|
21
|
-
end
|
22
|
-
|
23
|
-
@m_w %= 4294967296
|
24
|
-
@m_z %= 4294967296
|
25
|
-
end
|
26
|
-
|
27
|
-
# Produce a uniform random sample from the open interval (lower, upper).
|
28
|
-
# The method will not return either end point.
|
29
|
-
def uniform(lower = 0, upper = 1)
|
30
|
-
raise 'Invalid range' if upper <= lower
|
31
|
-
((get_unsigned_int + 1) * (upper - lower) / 4294967296.0) + lower
|
32
|
-
end
|
33
|
-
|
34
|
-
# Sample normal distribution with given mean and standard deviation
|
35
|
-
def normal(mean = 0.0, standard_deviation = 1.0)
|
36
|
-
raise 'Invalid standard deviation' if standard_deviation <= 0
|
37
|
-
mean + standard_deviation * ((-2.0 * Math.log(uniform)) ** 0.5) * Math.sin(2.0 * Math::PI * uniform)
|
38
|
-
end
|
39
|
-
|
40
|
-
# Get exponential random sample with specified mean
|
41
|
-
def exponential(mean = 1)
|
42
|
-
raise 'Mean must be positive' if mean <= 0
|
43
|
-
-1.0 * mean * Math.log(uniform)
|
44
|
-
end
|
45
|
-
|
46
|
-
# Get triangular random sample with specified lower limit, mode, upper limit
|
47
|
-
def triangular(lower, mode, upper)
|
48
|
-
raise 'Upper limit must be larger than lower limit' if upper < lower
|
49
|
-
raise 'Mode must lie between the upper and lower limits' if (mode < lower || mode > upper)
|
50
|
-
f_c = (mode - lower) / (upper - lower)
|
51
|
-
uniform_rand_num = uniform
|
52
|
-
if uniform_rand_num < f_c
|
53
|
-
lower + Math.sqrt(uniform_rand_num * (upper - lower) * (mode - lower))
|
54
|
-
else
|
55
|
-
upper - Math.sqrt((1 - uniform_rand_num) * (upper - lower) * (upper - mode))
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
# Implementation based on "A Simple Method for Generating Gamma Variables"
|
60
|
-
# by George Marsaglia and Wai Wan Tsang. ACM Transactions on Mathematical Software
|
61
|
-
# Vol 26, No 3, September 2000, pages 363-372.
|
62
|
-
def gamma(shape, scale)
|
63
|
-
if shape >= 1.0
|
64
|
-
d = shape - 1.0 / 3.0
|
65
|
-
c = 1 / ((9 * d) ** 0.5)
|
66
|
-
while true
|
67
|
-
v = 0.0
|
68
|
-
while v <= 0.0
|
69
|
-
x = normal
|
70
|
-
v = 1.0 + c * x
|
71
|
-
end
|
72
|
-
v = v ** 3
|
73
|
-
u = uniform
|
74
|
-
if u < (1.0 - 0.0331 * (x ** 4)) || Math.log(u) < (0.5 * (x ** 2) + d * (1.0 - v + Math.log(v)))
|
75
|
-
return scale * d * v
|
76
|
-
end
|
77
|
-
end
|
78
|
-
elsif shape <= 0.0
|
79
|
-
raise 'Shape must be positive'
|
80
|
-
else
|
81
|
-
g = gamma(shape + 1.0, 1.0)
|
82
|
-
w = uniform
|
83
|
-
return scale * g * (w ** (1.0 / shape))
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def chi_square(degrees_of_freedom)
|
88
|
-
gamma(0.5 * degrees_of_freedom, 2.0)
|
89
|
-
end
|
90
|
-
|
91
|
-
def inverse_gamma(shape, scale)
|
92
|
-
1.0 / gamma(shape, 1.0 / scale)
|
93
|
-
end
|
94
|
-
|
95
|
-
def beta(a, b)
|
96
|
-
raise "Alpha and beta parameters must be positive. Received a = #{a} and b = #{b}." unless a > 0 && b > 0
|
97
|
-
u = gamma(a, 1)
|
98
|
-
v = gamma(b, 1)
|
99
|
-
u / (u + v)
|
100
|
-
end
|
101
|
-
|
102
|
-
def weibull(shape, scale)
|
103
|
-
raise 'Shape and scale must be positive' if shape <= 0.0 || scale <= 0.0
|
104
|
-
|
105
|
-
scale * ((-Math.log(uniform)) ** (1.0 / shape))
|
106
|
-
end
|
107
|
-
|
108
|
-
def cauchy(median, scale)
|
109
|
-
raise 'Scale must be positive' if scale <= 0
|
110
|
-
|
111
|
-
median + scale * Math.tan(Math::PI * (uniform - 0.5))
|
112
|
-
end
|
113
|
-
|
114
|
-
def student_t(degrees_of_freedom)
|
115
|
-
raise 'Degrees of freedom must be positive' if degrees_of_freedom <= 0
|
116
|
-
|
117
|
-
normal / ((chi_square(degrees_of_freedom) / degrees_of_freedom) ** 0.5)
|
118
|
-
end
|
119
|
-
|
120
|
-
def laplace(mean, scale)
|
121
|
-
u = uniform
|
122
|
-
mean + Math.log(2) + ((u < 0.5 ? 1 : -1) * scale * Math.log(u < 0.5 ? u : 1 - u))
|
123
|
-
end
|
124
|
-
|
125
|
-
def log_normal(mu, sigma)
|
126
|
-
Math.exp(normal(mu, sigma))
|
127
|
-
end
|
128
|
-
|
129
|
-
def dirichlet(*parameters)
|
130
|
-
sample = parameters.map { |a| gamma(a, 1) }
|
131
|
-
sum = sample.inject(0.0) { |sum, g| sum + g }
|
132
|
-
sample.map { |g| g / sum }
|
133
|
-
end
|
134
|
-
|
135
|
-
private
|
136
|
-
|
137
|
-
# This is the heart of the generator.
|
138
|
-
# It uses George Marsaglia's MWC algorithm to produce an unsigned integer.
|
139
|
-
# See http://www.bobwheeler.com/statistics/Password/MarsagliaPost.txt
|
140
|
-
def get_unsigned_int
|
141
|
-
@m_z = 36969 * (@m_z & 65535) + (@m_z >> 16);
|
142
|
-
@m_w = 18000 * (@m_w & 65535) + (@m_w >> 16);
|
143
|
-
((@m_z << 16) + (@m_w & 65535)) % 4294967296
|
144
|
-
end
|
145
|
-
|
146
|
-
def gamma_function(x)
|
147
|
-
g = [
|
148
|
-
1.0,
|
149
|
-
0.5772156649015329,
|
150
|
-
-0.6558780715202538,
|
151
|
-
-0.420026350340952e-1,
|
152
|
-
0.1665386113822915,
|
153
|
-
-0.421977345555443e-1,
|
154
|
-
-0.9621971527877e-2,
|
155
|
-
0.7218943246663e-2,
|
156
|
-
-0.11651675918591e-2,
|
157
|
-
-0.2152416741149e-3,
|
158
|
-
0.1280502823882e-3,
|
159
|
-
-0.201348547807e-4,
|
160
|
-
-0.12504934821e-5,
|
161
|
-
0.1133027232e-5,
|
162
|
-
-0.2056338417e-6,
|
163
|
-
0.6116095e-8,
|
164
|
-
0.50020075e-8,
|
165
|
-
-0.11812746e-8,
|
166
|
-
0.1043427e-9,
|
167
|
-
0.77823e-11,
|
168
|
-
-0.36968e-11,
|
169
|
-
0.51e-12,
|
170
|
-
-0.206e-13,
|
171
|
-
-0.54e-14,
|
172
|
-
0.14e-14
|
173
|
-
]
|
174
|
-
|
175
|
-
r = 1.0
|
176
|
-
|
177
|
-
return 1e308 if x > 171.0
|
178
|
-
if x.is_a?(Fixnum) || x == x.to_i
|
179
|
-
if x > 0
|
180
|
-
ga = (2...x).inject(1.0) { |prod, i| prod * i }
|
181
|
-
else
|
182
|
-
1e308
|
183
|
-
end
|
184
|
-
else
|
185
|
-
if x.abs > 1.0
|
186
|
-
r = (1..(x.abs.to_i)).inject(1.0) { |prod, i| prod * (x.abs - i) }
|
187
|
-
z = x.abs - x.abs.to_i
|
188
|
-
else
|
189
|
-
z = x
|
190
|
-
end
|
191
|
-
|
192
|
-
gr = g[24]
|
193
|
-
23.downto(0).each do |i|
|
194
|
-
gr = gr * z + g[i]
|
195
|
-
end
|
196
|
-
ga = 1.0 / (gr * z)
|
197
|
-
if x.abs > 1
|
198
|
-
ga *= r
|
199
|
-
if x < 0
|
200
|
-
ga = -Math::PI / (x * ga * Math.sin(Math::PI * x))
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
ga
|
206
|
-
end
|
207
|
-
end
|
1
|
+
require 'simple-random/simple_random'
|
2
|
+
require 'simple-random/multi_threaded_simple_random'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'monitor'
|
2
|
+
|
3
|
+
class MultiThreadedSimpleRandom < SimpleRandom
|
4
|
+
class << self
|
5
|
+
@instances = nil
|
6
|
+
|
7
|
+
def instance
|
8
|
+
|
9
|
+
unless @instances
|
10
|
+
extend MonitorMixin
|
11
|
+
|
12
|
+
self.synchronize do
|
13
|
+
@instances ||= {}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
instance_id = Thread.current.object_id
|
18
|
+
|
19
|
+
unless @instances[instance_id]
|
20
|
+
self.synchronize do
|
21
|
+
@instances[instance_id] ||= new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
@instances[instance_id]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private_class_method :new
|
30
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
class SimpleRandom
|
2
|
+
def initialize
|
3
|
+
@m_w = 521288629
|
4
|
+
@m_z = 362436069
|
5
|
+
end
|
6
|
+
|
7
|
+
def set_seed(*args)
|
8
|
+
if args.size > 1
|
9
|
+
@m_w = args.first.to_i if args.first.to_i != 0
|
10
|
+
@m_z = args.last.to_i if args.last.to_i != 0
|
11
|
+
elsif args.first.is_a?(Numeric)
|
12
|
+
@m_w = args.first.to_i if args.first.to_i != 0
|
13
|
+
elsif args.first.is_a?(Time)
|
14
|
+
x = (args.first.to_f * 1000000).to_i
|
15
|
+
@m_w = x >> 16
|
16
|
+
@m_z = x % 4294967296 # 2 ** 32
|
17
|
+
else
|
18
|
+
x = (Time.now.to_f * 1000000).to_i
|
19
|
+
@m_w = x >> 16
|
20
|
+
@m_z = x % 4294967296 # 2 ** 32
|
21
|
+
end
|
22
|
+
|
23
|
+
@m_w %= 4294967296
|
24
|
+
@m_z %= 4294967296
|
25
|
+
end
|
26
|
+
|
27
|
+
# Produce a uniform random sample from the open interval (lower, upper).
|
28
|
+
# The method will not return either end point.
|
29
|
+
def uniform(lower = 0, upper = 1)
|
30
|
+
raise 'Invalid range' if upper <= lower
|
31
|
+
((get_unsigned_int + 1) * (upper - lower) / 4294967296.0) + lower
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sample normal distribution with given mean and standard deviation
|
35
|
+
def normal(mean = 0.0, standard_deviation = 1.0)
|
36
|
+
raise 'Invalid standard deviation' if standard_deviation <= 0
|
37
|
+
mean + standard_deviation * ((-2.0 * Math.log(uniform)) ** 0.5) * Math.sin(2.0 * Math::PI * uniform)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get exponential random sample with specified mean
|
41
|
+
def exponential(mean = 1)
|
42
|
+
raise 'Mean must be positive' if mean <= 0
|
43
|
+
-1.0 * mean * Math.log(uniform)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get triangular random sample with specified lower limit, mode, upper limit
|
47
|
+
def triangular(lower, mode, upper)
|
48
|
+
raise 'Upper limit must be larger than lower limit' if upper < lower
|
49
|
+
raise 'Mode must lie between the upper and lower limits' if (mode < lower || mode > upper)
|
50
|
+
f_c = (mode - lower) / (upper - lower)
|
51
|
+
uniform_rand_num = uniform
|
52
|
+
if uniform_rand_num < f_c
|
53
|
+
lower + Math.sqrt(uniform_rand_num * (upper - lower) * (mode - lower))
|
54
|
+
else
|
55
|
+
upper - Math.sqrt((1 - uniform_rand_num) * (upper - lower) * (upper - mode))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Implementation based on "A Simple Method for Generating Gamma Variables"
|
60
|
+
# by George Marsaglia and Wai Wan Tsang. ACM Transactions on Mathematical Software
|
61
|
+
# Vol 26, No 3, September 2000, pages 363-372.
|
62
|
+
def gamma(shape, scale)
|
63
|
+
if shape >= 1.0
|
64
|
+
d = shape - 1.0 / 3.0
|
65
|
+
c = 1 / ((9 * d) ** 0.5)
|
66
|
+
while true
|
67
|
+
v = 0.0
|
68
|
+
while v <= 0.0
|
69
|
+
x = normal
|
70
|
+
v = 1.0 + c * x
|
71
|
+
end
|
72
|
+
v = v ** 3
|
73
|
+
u = uniform
|
74
|
+
if u < (1.0 - 0.0331 * (x ** 4)) || Math.log(u) < (0.5 * (x ** 2) + d * (1.0 - v + Math.log(v)))
|
75
|
+
return scale * d * v
|
76
|
+
end
|
77
|
+
end
|
78
|
+
elsif shape <= 0.0
|
79
|
+
raise 'Shape must be positive'
|
80
|
+
else
|
81
|
+
g = gamma(shape + 1.0, 1.0)
|
82
|
+
w = uniform
|
83
|
+
return scale * g * (w ** (1.0 / shape))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def chi_square(degrees_of_freedom)
|
88
|
+
gamma(0.5 * degrees_of_freedom, 2.0)
|
89
|
+
end
|
90
|
+
|
91
|
+
def inverse_gamma(shape, scale)
|
92
|
+
1.0 / gamma(shape, 1.0 / scale)
|
93
|
+
end
|
94
|
+
|
95
|
+
def beta(a, b)
|
96
|
+
raise "Alpha and beta parameters must be positive. Received a = #{a} and b = #{b}." unless a > 0 && b > 0
|
97
|
+
u = gamma(a, 1)
|
98
|
+
v = gamma(b, 1)
|
99
|
+
u / (u + v)
|
100
|
+
end
|
101
|
+
|
102
|
+
def weibull(shape, scale)
|
103
|
+
raise 'Shape and scale must be positive' if shape <= 0.0 || scale <= 0.0
|
104
|
+
|
105
|
+
scale * ((-Math.log(uniform)) ** (1.0 / shape))
|
106
|
+
end
|
107
|
+
|
108
|
+
def cauchy(median, scale)
|
109
|
+
raise 'Scale must be positive' if scale <= 0
|
110
|
+
|
111
|
+
median + scale * Math.tan(Math::PI * (uniform - 0.5))
|
112
|
+
end
|
113
|
+
|
114
|
+
def student_t(degrees_of_freedom)
|
115
|
+
raise 'Degrees of freedom must be positive' if degrees_of_freedom <= 0
|
116
|
+
|
117
|
+
normal / ((chi_square(degrees_of_freedom) / degrees_of_freedom) ** 0.5)
|
118
|
+
end
|
119
|
+
|
120
|
+
def laplace(mean, scale)
|
121
|
+
u = uniform
|
122
|
+
mean + Math.log(2) + ((u < 0.5 ? 1 : -1) * scale * Math.log(u < 0.5 ? u : 1 - u))
|
123
|
+
end
|
124
|
+
|
125
|
+
def log_normal(mu, sigma)
|
126
|
+
Math.exp(normal(mu, sigma))
|
127
|
+
end
|
128
|
+
|
129
|
+
def dirichlet(*parameters)
|
130
|
+
sample = parameters.map { |a| gamma(a, 1) }
|
131
|
+
sum = sample.inject(0.0) { |sum, g| sum + g }
|
132
|
+
sample.map { |g| g / sum }
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
# This is the heart of the generator.
|
138
|
+
# It uses George Marsaglia's MWC algorithm to produce an unsigned integer.
|
139
|
+
# See http://www.bobwheeler.com/statistics/Password/MarsagliaPost.txt
|
140
|
+
def get_unsigned_int
|
141
|
+
@m_z = 36969 * (@m_z & 65535) + (@m_z >> 16);
|
142
|
+
@m_w = 18000 * (@m_w & 65535) + (@m_w >> 16);
|
143
|
+
((@m_z << 16) + (@m_w & 65535)) % 4294967296
|
144
|
+
end
|
145
|
+
|
146
|
+
def gamma_function(x)
|
147
|
+
g = [
|
148
|
+
1.0,
|
149
|
+
0.5772156649015329,
|
150
|
+
-0.6558780715202538,
|
151
|
+
-0.420026350340952e-1,
|
152
|
+
0.1665386113822915,
|
153
|
+
-0.421977345555443e-1,
|
154
|
+
-0.9621971527877e-2,
|
155
|
+
0.7218943246663e-2,
|
156
|
+
-0.11651675918591e-2,
|
157
|
+
-0.2152416741149e-3,
|
158
|
+
0.1280502823882e-3,
|
159
|
+
-0.201348547807e-4,
|
160
|
+
-0.12504934821e-5,
|
161
|
+
0.1133027232e-5,
|
162
|
+
-0.2056338417e-6,
|
163
|
+
0.6116095e-8,
|
164
|
+
0.50020075e-8,
|
165
|
+
-0.11812746e-8,
|
166
|
+
0.1043427e-9,
|
167
|
+
0.77823e-11,
|
168
|
+
-0.36968e-11,
|
169
|
+
0.51e-12,
|
170
|
+
-0.206e-13,
|
171
|
+
-0.54e-14,
|
172
|
+
0.14e-14
|
173
|
+
]
|
174
|
+
|
175
|
+
r = 1.0
|
176
|
+
|
177
|
+
return 1e308 if x > 171.0
|
178
|
+
if x.is_a?(Fixnum) || x == x.to_i
|
179
|
+
if x > 0
|
180
|
+
ga = (2...x).inject(1.0) { |prod, i| prod * i }
|
181
|
+
else
|
182
|
+
1e308
|
183
|
+
end
|
184
|
+
else
|
185
|
+
if x.abs > 1.0
|
186
|
+
r = (1..(x.abs.to_i)).inject(1.0) { |prod, i| prod * (x.abs - i) }
|
187
|
+
z = x.abs - x.abs.to_i
|
188
|
+
else
|
189
|
+
z = x
|
190
|
+
end
|
191
|
+
|
192
|
+
gr = g[24]
|
193
|
+
23.downto(0).each do |i|
|
194
|
+
gr = gr * z + g[i]
|
195
|
+
end
|
196
|
+
ga = 1.0 / (gr * z)
|
197
|
+
if x.abs > 1
|
198
|
+
ga *= r
|
199
|
+
if x < 0
|
200
|
+
ga = -Math::PI / (x * ga * Math.sin(Math::PI * x))
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
ga
|
206
|
+
end
|
207
|
+
end
|