simple-random 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 65923da1d24e7888037400e04e8247ce5a65e223
4
- data.tar.gz: c1340f384268823745fdaefe3f0a3b8a068539d7
3
+ metadata.gz: e4aaf55a487de9bfbbaf5352472cd4d9b3888ab5
4
+ data.tar.gz: 21ba3e7c6b8337a21f8c3ad6d5e7a50f2527d792
5
5
  SHA512:
6
- metadata.gz: 99f369a1a00047572b031eae49326f458a2afa8944c8ccfeb03f02872ea1bedfed70939631ee84ce72aee9eabdcdf062e4911ba71f41d0f8dcaf7e8f8da7d271
7
- data.tar.gz: 9750c6caa2986f42a77333c87f9c6d436c49c9fc5282f77d61f8b9cb1eb6a6fe874bf097b72a623c0101fdb295ceb0093ff1c348a6c0dd01f69ec26f7e2fd428
6
+ metadata.gz: 55d1f2eee8febbd9b295065fbd8956bdc0bbdbd92d60ff46aa5cd2aeff8f7e0ad12dd991a84518308395feb4b194da9ebcd9851f539f51461bd3b99ccf9f8619
7
+ data.tar.gz: 3321d47d01c0b6a6692b7958c4bd4d90d3baa0b82621f318910fb14285aa3aa16df7478582a2072ca7b42636aef2e24cca08a90898484ec75c7bc7767fa786dd
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ source "http://rubygems.org"
6
6
  # Add dependencies to develop your gem here.
7
7
  # Include everything needed to run rake, tests, features, etc.
8
8
  group :development do
9
+ gem "awesome_print", ">= 1.6"
9
10
  gem "minitest", ">= 0"
10
11
  gem "shoulda", ">= 0"
11
12
  gem "rdoc", "~> 3.12"
@@ -8,6 +8,7 @@ GEM
8
8
  thread_safe (~> 0.1)
9
9
  tzinfo (~> 1.1)
10
10
  addressable (2.3.6)
11
+ awesome_print (1.6.1)
11
12
  builder (3.2.2)
12
13
  descendants_tracker (0.0.4)
13
14
  thread_safe (~> 0.3, >= 0.3.1)
@@ -73,9 +74,13 @@ PLATFORMS
73
74
  ruby
74
75
 
75
76
  DEPENDENCIES
77
+ awesome_print (>= 1.6)
76
78
  bundler (~> 1.0)
77
79
  jeweler (~> 2.0.1)
78
80
  minitest
79
81
  rdoc (~> 3.12)
80
82
  shoulda
81
83
  simplecov
84
+
85
+ BUNDLED WITH
86
+ 1.10.6
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.2
1
+ 1.0.3
@@ -1,2 +1,2 @@
1
- require 'simple-random/simple_random'
2
- require 'simple-random/multi_threaded_simple_random'
1
+ require File.join(File.dirname(__FILE__), 'simple-random', 'simple_random')
2
+ require File.join(File.dirname(__FILE__), 'simple-random', 'multi_threaded_simple_random')
@@ -1,54 +1,58 @@
1
1
  class SimpleRandom
2
+ class InvalidSeedArgument < StandardError; end
3
+
4
+ C_32_BIT = 4294967296
5
+ F_32_BIT = 4294967296.0
6
+
2
7
  def initialize
3
8
  @m_w = 521288629
4
9
  @m_z = 362436069
5
10
  end
6
11
 
7
12
  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
13
+ validate_seeds!(*args)
14
+
15
+ @m_w, @m_z = if args.size > 1
16
+ args[0..1].map(&:to_i)
11
17
  elsif args.first.is_a?(Numeric)
12
- @m_z = 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
18
+ [@m_w, args.first.to_i]
17
19
  else
18
- x = (Time.now.to_f * 1000000).to_i
19
- @m_w = x >> 16
20
- @m_z = x % 4294967296 # 2 ** 32
20
+ generate_temporal_seed(args.first || Time.now)
21
21
  end
22
22
 
23
- @m_w %= 4294967296
24
- @m_z %= 4294967296
23
+ @m_w %= C_32_BIT
24
+ @m_z %= C_32_BIT
25
25
  end
26
26
 
27
27
  # Produce a uniform random sample from the open interval (lower, upper).
28
- # The method will not return either end point.
29
28
  def uniform(lower = 0, upper = 1)
30
- raise 'Invalid range' if upper <= lower
31
- ((get_unsigned_int + 1) * (upper - lower) / 4294967296.0) + lower
29
+ fail ArgumentError, 'Upper bound must be greater than lower bound.' unless lower < upper
30
+
31
+ ((get_unsigned_int + 1) * (upper - lower) / F_32_BIT) + lower
32
32
  end
33
33
 
34
34
  # Sample normal distribution with given mean and standard deviation
35
35
  def normal(mean = 0.0, standard_deviation = 1.0)
36
- raise 'Invalid standard deviation' if standard_deviation <= 0
36
+ fail ArgumentError, 'Standard deviation must be strictly positive' unless standard_deviation > 0
37
+
37
38
  mean + standard_deviation * ((-2.0 * Math.log(uniform)) ** 0.5) * Math.sin(2.0 * Math::PI * uniform)
38
39
  end
39
40
 
40
41
  # Get exponential random sample with specified mean
41
42
  def exponential(mean = 1)
42
- raise 'Mean must be positive' if mean <= 0
43
+ fail ArgumentError, "Mean must be strictly positive" unless mean > 0
44
+
43
45
  -1.0 * mean * Math.log(uniform)
44
46
  end
45
47
 
46
48
  # Get triangular random sample with specified lower limit, mode, upper limit
47
49
  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
+ fail ArgumentError, 'Upper bound must be greater than lower bound.' unless lower < upper
51
+ fail ArgumentError, 'Mode must lie between the upper and lower limits' if mode > upper || mode < lower
52
+
50
53
  f_c = (mode - lower) / (upper - lower)
51
54
  uniform_rand_num = uniform
55
+
52
56
  if uniform_rand_num < f_c
53
57
  lower + Math.sqrt(uniform_rand_num * (upper - lower) * (mode - lower))
54
58
  else
@@ -60,28 +64,31 @@ class SimpleRandom
60
64
  # by George Marsaglia and Wai Wan Tsang. ACM Transactions on Mathematical Software
61
65
  # Vol 26, No 3, September 2000, pages 363-372.
62
66
  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'
67
+ fail ArgumentError, 'Shape must be strictly positive' unless shape > 0
68
+
69
+ base = if shape < 1
70
+ gamma(shape + 1.0, 1.0) * uniform ** -shape
80
71
  else
81
- g = gamma(shape + 1.0, 1.0)
82
- w = uniform
83
- return scale * g * (w ** (1.0 / shape))
72
+ d = shape - 1 / 3.0
73
+ c = (9 * d) ** -0.5
74
+
75
+ begin
76
+ z = normal
77
+
78
+ condition1 = z > (-1.0 / c)
79
+ condition2 = false
80
+
81
+ if condition1
82
+ u = uniform
83
+ v = (1 + c * z) ** 3
84
+ condition2 = Math.log(u) < (0.5 * (z ** 2) + d * (1.0 - v + Math.log(v)))
85
+ end
86
+ end while !condition2
87
+
88
+ d * v
84
89
  end
90
+
91
+ scale * base
85
92
  end
86
93
 
87
94
  def chi_square(degrees_of_freedom)
@@ -93,34 +100,36 @@ class SimpleRandom
93
100
  end
94
101
 
95
102
  def beta(a, b)
96
- raise "Alpha and beta parameters must be positive. Received a = #{a} and b = #{b}." unless a > 0 && b > 0
103
+ fail ArgumentError, "Parameters must be strictly positive" unless a > 0 && b > 0
97
104
  u = gamma(a, 1)
98
105
  v = gamma(b, 1)
99
106
  u / (u + v)
100
107
  end
101
108
 
102
109
  def weibull(shape, scale)
103
- raise 'Shape and scale must be positive' if shape <= 0.0 || scale <= 0.0
110
+ fail ArgumentError, 'Shape and scale must be positive' unless shape > 0 && scale > 0
104
111
 
105
112
  scale * ((-Math.log(uniform)) ** (1.0 / shape))
106
113
  end
107
114
 
108
115
  def cauchy(median, scale)
109
- raise 'Scale must be positive' if scale <= 0
116
+ fail ArgumentError, 'Scale must be positive' unless scale > 0
110
117
 
111
118
  median + scale * Math.tan(Math::PI * (uniform - 0.5))
112
119
  end
113
120
 
114
121
  def student_t(degrees_of_freedom)
115
- raise 'Degrees of freedom must be positive' if degrees_of_freedom <= 0
122
+ fail ArgumentError, 'Degrees of freedom must be strictly positive' unless degrees_of_freedom > 0
116
123
 
117
124
  normal / ((chi_square(degrees_of_freedom) / degrees_of_freedom) ** 0.5)
118
125
  end
119
126
 
120
127
  def laplace(mean, scale)
121
- u_1 = uniform
128
+ u_1 = uniform(-0.5, 0.5)
122
129
  u_2 = uniform
123
- mean + ((u_1 < 0.5 ? 1 : -1) * scale * Math.log( 1-2*(u_2-0.5).abs))
130
+
131
+ sign = u_1 / u_1.abs
132
+ mean + sign * scale * Math.log(1 - u_2)
124
133
  end
125
134
 
126
135
  def log_normal(mu, sigma)
@@ -144,65 +153,85 @@ class SimpleRandom
144
153
  ((@m_z << 16) + (@m_w & 65535)) % 4294967296
145
154
  end
146
155
 
147
- def gamma_function(x)
148
- g = [
149
- 1.0,
150
- 0.5772156649015329,
151
- -0.6558780715202538,
152
- -0.420026350340952e-1,
153
- 0.1665386113822915,
154
- -0.421977345555443e-1,
155
- -0.9621971527877e-2,
156
- 0.7218943246663e-2,
157
- -0.11651675918591e-2,
158
- -0.2152416741149e-3,
159
- 0.1280502823882e-3,
160
- -0.201348547807e-4,
161
- -0.12504934821e-5,
162
- 0.1133027232e-5,
163
- -0.2056338417e-6,
164
- 0.6116095e-8,
165
- 0.50020075e-8,
166
- -0.11812746e-8,
167
- 0.1043427e-9,
168
- 0.77823e-11,
169
- -0.36968e-11,
170
- 0.51e-12,
171
- -0.206e-13,
172
- -0.54e-14,
173
- 0.14e-14
174
- ]
175
-
176
- r = 1.0
156
+ def validate_seeds!(*args)
157
+ return true if args.compact.empty?
158
+
159
+ unless args[0].to_f.abs > 0
160
+ fail InvalidSeedArgument, 'Seeds must be strictly positive'
161
+ end
162
+
163
+ unless args[1].nil? || args[1].to_f.abs > 0
164
+ fail InvalidSeedArgument, 'Seeds must be strictly positive'
165
+ end
166
+
167
+ true
168
+ end
169
+
170
+ def generate_temporal_seed(timestamp = Time.now)
171
+ x = (timestamp.to_f * 1000000).to_i
177
172
 
173
+ [x >> 16, x % 4294967296]
174
+ end
175
+
176
+ def gamma_function(x)
178
177
  return 1e308 if x > 171.0
179
- if x.is_a?(Fixnum) || x == x.to_i
180
- if x > 0
181
- ga = (2...x).inject(1.0) { |prod, i| prod * i }
182
- else
183
- 1e308
184
- end
178
+
179
+ if x.to_f == x.to_i
180
+ return unless x > 0
181
+ return 1 if x.to_i == 1
182
+
183
+ (1...x).inject(&:*)
185
184
  else
186
- if x.abs > 1.0
187
- r = (1..(x.abs.to_i)).inject(1.0) { |prod, i| prod * (x.abs - i) }
188
- z = x.abs - x.abs.to_i
185
+ z = if x.abs > 1.0
186
+ x.abs - x.abs.to_i
189
187
  else
190
- z = x
188
+ x
191
189
  end
192
190
 
193
- gr = g[24]
194
- 23.downto(0).each do |i|
195
- gr = gr * z + g[i]
191
+ gr = GAMMA_VALUES.inject(GAMMA_NAUGHT) do |sum, g|
192
+ sum * z + g
196
193
  end
197
- ga = 1.0 / (gr * z)
198
- if x.abs > 1
199
- ga *= r
200
- if x < 0
201
- ga = -Math::PI / (x * ga * Math.sin(Math::PI * x))
202
- end
194
+
195
+ r = if x.abs > 1
196
+ (1..(x.abs.to_i)).inject(1.0) { |prod, i| prod * (x.abs - i) }
197
+ else
198
+ 1.0
203
199
  end
204
- end
205
200
 
206
- ga
201
+ if x < 0 && x.abs > 1
202
+ -Math::PI * gr * z / (x * r * Math.sin(Math::PI * x))
203
+ else
204
+ r / (gr * z)
205
+ end
206
+ end
207
207
  end
208
+
209
+ GAMMA_NAUGHT = 0.14e-14
210
+
211
+ GAMMA_VALUES = [
212
+ -5.4e-15,
213
+ -2.06e-14,
214
+ 5.1e-13,
215
+ -3.6968e-12,
216
+ 7.7823e-12,
217
+ 1.043427e-10,
218
+ -1.1812746e-09,
219
+ 5.0020075e-09,
220
+ 6.116095e-09,
221
+ -2.056338417e-07,
222
+ 1.133027232e-06,
223
+ -1.2504934821e-06,
224
+ -2.01348547807e-05,
225
+ 0.0001280502823882,
226
+ -0.0002152416741149,
227
+ -0.0011651675918591,
228
+ 0.007218943246663,
229
+ -0.009621971527877,
230
+ -0.0421977345555443,
231
+ 0.1665386113822915,
232
+ -0.0420026350340952,
233
+ -0.6558780715202538,
234
+ 0.5772156649015329,
235
+ 1.0
236
+ ]
208
237
  end
@@ -200,7 +200,7 @@ class TestSimpleRandom < MiniTest::Test
200
200
 
201
201
  assert epsilon < MAXIMUM_EPSILON
202
202
  end
203
-
203
+
204
204
  should "generate a random number sampled from a gamma distribution" do
205
205
  assert @r.gamma(5, 2.3)
206
206
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple-random
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - John D. Cook
@@ -9,8 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-11-24 00:00:00.000000000 Z
12
+ date: 2015-11-25 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: awesome_print
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '1.6'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '1.6'
14
28
  - !ruby/object:Gem::Dependency
15
29
  name: minitest
16
30
  requirement: !ruby/object:Gem::Requirement