simple-random 1.0.2 → 1.0.3

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 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