simple-random 0.10.0 → 1.0.0
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 +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
|