loaded_die 1.0.2 → 2.0.1
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.
- data/LICENSE.txt +1 -1
- data/README.txt +47 -0
- data/lib/loaded_die.rb +53 -52
- data/test/loaded_die_test.rb +172 -46
- metadata +47 -24
- checksums.yaml +0 -7
- data/README.rdoc +0 -57
- data/loaded_die.gemspec +0 -19
data/LICENSE.txt
CHANGED
data/README.txt
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Loaded Die
|
|
2
|
+
|
|
3
|
+
Loaded Die is a Ruby library that makes it easy to randomly choose from a set
|
|
4
|
+
of options where some options are more likely than others.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Usage
|
|
8
|
+
|
|
9
|
+
This is the basic pattern: First, you load the library. Then you create an
|
|
10
|
+
instance of LoadedDie::Sampler and send the sample message to it as many
|
|
11
|
+
times as you like.
|
|
12
|
+
|
|
13
|
+
Let's say you want to choose randomly from three strings: "A", "B", and "C".
|
|
14
|
+
You want "A" and "B" to be equally likely, and "C" to be twice as likely as one
|
|
15
|
+
of them. That means "A" should be 25% likely, B should be 25% likely, and C
|
|
16
|
+
should be 50% likely.
|
|
17
|
+
|
|
18
|
+
You can create your sampler like this:
|
|
19
|
+
|
|
20
|
+
require "loaded_die"
|
|
21
|
+
sampler = LoadedDie::Sampler.new({ "A" => 0.25, "B" => 0.25, "C" => 0.5 })
|
|
22
|
+
|
|
23
|
+
And then sample from it like this:
|
|
24
|
+
|
|
25
|
+
sampler.sample
|
|
26
|
+
|
|
27
|
+
But you don't need to be fussy about how you represent probabilities. That is,
|
|
28
|
+
you don't need to represent them as numbers between zero and one such that they
|
|
29
|
+
sum to one. Their relative values are what matter. Furthermore, they don't need
|
|
30
|
+
to be floats, just objects that convert to floats. So you could also create
|
|
31
|
+
your sampler like this:
|
|
32
|
+
|
|
33
|
+
sampler = LoadedDie::Sampler.new({ "A" => 1, "B" => 1, "C" => 2 })
|
|
34
|
+
|
|
35
|
+
You can specify the random number generator that sample uses. To do this,
|
|
36
|
+
supply as the argument a hash with your random number generator under the
|
|
37
|
+
:random key. (This is based on the behavior of Ruby's Array#sample.) Your
|
|
38
|
+
random number generator will be sent a rand message with one argument, the sum
|
|
39
|
+
of the weights you specified when creating the sampler. The returned object
|
|
40
|
+
must be a float greater than or equal to zero and less than the sum of the
|
|
41
|
+
weights.
|
|
42
|
+
|
|
43
|
+
rng = Object.new
|
|
44
|
+
def rng.rand(n)
|
|
45
|
+
0.0
|
|
46
|
+
end
|
|
47
|
+
sampler.sample({ :random => rng })
|
data/lib/loaded_die.rb
CHANGED
|
@@ -1,80 +1,79 @@
|
|
|
1
1
|
##
|
|
2
|
-
# This module
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
module LoadedDie
|
|
2
|
+
# This module allows you to choose randomly from a set of options where some
|
|
3
|
+
# options are more likely than others.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
# The version string.
|
|
9
|
-
#
|
|
10
|
-
VERSION = '1.0.2'
|
|
5
|
+
module LoadedDie
|
|
11
6
|
|
|
12
7
|
##
|
|
13
|
-
#
|
|
8
|
+
# Instances of this class can choose randomly from a set of options (called
|
|
14
9
|
# individuals here). The options can have different probabilities of being
|
|
15
10
|
# chosen.
|
|
16
|
-
|
|
11
|
+
|
|
17
12
|
class Sampler
|
|
18
13
|
|
|
19
14
|
##
|
|
20
|
-
# The default random number generator.
|
|
21
|
-
#
|
|
15
|
+
# The default random number generator. It samples from a uniform
|
|
16
|
+
# distribution.
|
|
17
|
+
|
|
22
18
|
DEFAULT_RNG = ::Object.new
|
|
23
|
-
def DEFAULT_RNG.rand
|
|
19
|
+
def DEFAULT_RNG.rand n
|
|
24
20
|
::Kernel.rand * n
|
|
25
21
|
end
|
|
26
22
|
|
|
27
23
|
##
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
|
|
24
|
+
# Creates a sampler from a population. The argument should return an
|
|
25
|
+
# enumerable (conventionally an instance of Enumerator) of arrays when sent
|
|
26
|
+
# a to_enum message with no arguments. (A normal instance of Hash
|
|
27
|
+
# qualifies.) Each array must have at least two elements: the first element
|
|
28
|
+
# is an individual that can be chosen and the second element is its weight
|
|
29
|
+
# -- that is, the likelihood relative to the other weights that the
|
|
30
|
+
# individual will be chosen. Weights must convert to finite floats that are
|
|
31
|
+
# zero or positive.
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@compiled = population.inject [] do |accum, (individual, weight)|
|
|
42
|
-
if weight <= 0
|
|
43
|
-
raise ::ArgumentError, "non-positive weight #{weight}"
|
|
33
|
+
def initialize population
|
|
34
|
+
@compiled = population.to_enum.inject [] do |accum, elem|
|
|
35
|
+
individual = elem.fetch 0
|
|
36
|
+
weight = elem.fetch 1
|
|
37
|
+
weight_f = ::Kernel.Float weight
|
|
38
|
+
unless 0.0 <= weight_f && weight_f.finite?
|
|
39
|
+
::Kernel.raise ::ArgumentError, "invalid weight #{weight_f}"
|
|
40
|
+
break
|
|
44
41
|
end
|
|
45
42
|
prev_max =
|
|
46
43
|
if last = accum.last
|
|
47
|
-
last.maximum
|
|
44
|
+
last.fetch :maximum
|
|
48
45
|
else
|
|
49
|
-
0
|
|
46
|
+
0.0
|
|
50
47
|
end
|
|
51
|
-
accum <<
|
|
48
|
+
accum << { :maximum => prev_max + weight_f, :individual => individual }
|
|
52
49
|
end
|
|
53
50
|
nil
|
|
54
51
|
end
|
|
55
52
|
|
|
56
53
|
##
|
|
57
54
|
# Returns the sum of weights.
|
|
58
|
-
|
|
55
|
+
|
|
59
56
|
def length
|
|
60
57
|
if last = @compiled.last
|
|
61
|
-
last.maximum
|
|
58
|
+
last.fetch :maximum
|
|
62
59
|
else
|
|
63
|
-
0
|
|
60
|
+
0.0
|
|
64
61
|
end
|
|
65
62
|
end
|
|
66
63
|
|
|
67
64
|
##
|
|
68
|
-
# Returns the individual
|
|
69
|
-
# If it is greater than or equal to zero
|
|
70
|
-
#
|
|
71
|
-
# this returns nil.
|
|
72
|
-
|
|
73
|
-
def []
|
|
74
|
-
|
|
65
|
+
# Returns the individual associated with the given point on a line. The
|
|
66
|
+
# point should convert to a float. If it is greater than or equal to zero
|
|
67
|
+
# and less than the sum of weights, this returns the corresponding
|
|
68
|
+
# individual. If it is outside those bounds, this returns nil.
|
|
69
|
+
|
|
70
|
+
def [] point
|
|
71
|
+
point_f = ::Kernel.Float point
|
|
72
|
+
if 0.0 > point_f
|
|
75
73
|
nil
|
|
76
|
-
elsif choice = @compiled.detect { |segment|
|
|
77
|
-
|
|
74
|
+
elsif choice = @compiled.detect { |segment|
|
|
75
|
+
segment.fetch(:maximum) > point_f }
|
|
76
|
+
choice.fetch :individual
|
|
78
77
|
else
|
|
79
78
|
nil
|
|
80
79
|
end
|
|
@@ -82,16 +81,18 @@ module LoadedDie
|
|
|
82
81
|
|
|
83
82
|
##
|
|
84
83
|
# Returns a randomly-chosen individual. The argument is a hash, empty by
|
|
85
|
-
# default. If it contains a value for the +:random+ key, that value will
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
# sum of weights.
|
|
90
|
-
|
|
91
|
-
def sample
|
|
92
|
-
rng = options.fetch(:random) { DEFAULT_RNG }
|
|
93
|
-
point = rng.rand
|
|
84
|
+
# default. If it contains a value for the +:random+ key, that value will be
|
|
85
|
+
# used as the random number generator instead of the default. This RNG will
|
|
86
|
+
# be sent a rand message with one argument, the sum of weights, and should
|
|
87
|
+
# return a number (convertible to a float) greater than or equal to zero
|
|
88
|
+
# and less than the sum of weights.
|
|
89
|
+
|
|
90
|
+
def sample options = {}
|
|
91
|
+
rng = options.fetch(:random) { ::LoadedDie::Sampler::DEFAULT_RNG }
|
|
92
|
+
point = rng.rand length
|
|
94
93
|
self[point]
|
|
95
94
|
end
|
|
95
|
+
|
|
96
96
|
end
|
|
97
|
+
|
|
97
98
|
end
|
data/test/loaded_die_test.rb
CHANGED
|
@@ -1,69 +1,195 @@
|
|
|
1
|
-
|
|
1
|
+
# Sections that create local variables are in class definitions so that later
|
|
2
|
+
# sections cannot accidentally reference those local variables.
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
require "loaded_die"
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
# LoadedDie should be an instance of Module.
|
|
7
|
+
fail "" unless Module.equal? LoadedDie.class
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
# LoadedDie::Sampler should be a class.
|
|
10
|
+
fail "" unless Class.equal? LoadedDie::Sampler.class
|
|
8
11
|
|
|
12
|
+
# LoadedDie::Sampler::DEFAULT_RNG.rand should return a float in the expected
|
|
13
|
+
# range.
|
|
14
|
+
class ::Object
|
|
15
|
+
n = LoadedDie::Sampler::DEFAULT_RNG.rand 0.5
|
|
16
|
+
fail "" unless Float.equal? n.class
|
|
17
|
+
fail "" unless 0.0 <= n && 0.5 > n
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# LoadedDie::Sampler.new with an empty population should return an object of
|
|
21
|
+
# class LoadedDie::Sampler.
|
|
22
|
+
fail "" unless LoadedDie::Sampler.equal? LoadedDie::Sampler.new({}).class
|
|
23
|
+
|
|
24
|
+
# LoadedDie::Sampler.new with only zero or positive finite weights should
|
|
25
|
+
# return an instance of LoadedDie::Sampler.
|
|
26
|
+
fail "" unless LoadedDie::Sampler.equal?(
|
|
27
|
+
LoadedDie::Sampler.new({ :a => 0.0, :b => 1.0 }).class)
|
|
28
|
+
|
|
29
|
+
# LoadedDie::Sampler.new with a negative weight should raise an exception.
|
|
30
|
+
begin
|
|
31
|
+
LoadedDie::Sampler.new({ :a => -1.0 })
|
|
32
|
+
rescue ArgumentError
|
|
33
|
+
fail "" unless /\Ainvalid weight / =~ $!.message
|
|
34
|
+
else
|
|
35
|
+
fail ""
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# LoadedDie::Sampler.new with a NaN weight should raise an exception.
|
|
9
39
|
begin
|
|
10
|
-
LoadedDie::Sampler.new(:a => 0)
|
|
40
|
+
LoadedDie::Sampler.new({ :a => 0.0 / 0.0 })
|
|
11
41
|
rescue ArgumentError
|
|
42
|
+
fail "" unless /\Ainvalid weight / =~ $!.message
|
|
12
43
|
else
|
|
13
|
-
fail
|
|
44
|
+
fail ""
|
|
14
45
|
end
|
|
15
46
|
|
|
47
|
+
# LoadedDie::Sampler.new with an infinite weight should raise an exception.
|
|
16
48
|
begin
|
|
17
|
-
LoadedDie::Sampler.new(:a =>
|
|
49
|
+
LoadedDie::Sampler.new({ :a => 1.0 / 0.0 })
|
|
18
50
|
rescue ArgumentError
|
|
51
|
+
fail "" unless /\Ainvalid weight / =~ $!.message
|
|
19
52
|
else
|
|
20
|
-
fail
|
|
53
|
+
fail ""
|
|
21
54
|
end
|
|
22
55
|
|
|
23
|
-
|
|
56
|
+
# LoadedDie::Sampler.new with a weight that does not convert to a float should
|
|
57
|
+
# raise an exception.
|
|
58
|
+
begin
|
|
59
|
+
LoadedDie::Sampler.new({ :a => nil })
|
|
60
|
+
rescue TypeError
|
|
61
|
+
else
|
|
62
|
+
fail ""
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# LoadedDie::Sampler.new with a population that doesn't conform to the
|
|
66
|
+
# interface should raise an exception.
|
|
67
|
+
begin
|
|
68
|
+
LoadedDie::Sampler.new nil
|
|
69
|
+
rescue NoMethodError
|
|
70
|
+
else
|
|
71
|
+
fail ""
|
|
72
|
+
end
|
|
73
|
+
begin
|
|
74
|
+
LoadedDie::Sampler.new [[]]
|
|
75
|
+
rescue IndexError
|
|
76
|
+
else
|
|
77
|
+
fail ""
|
|
78
|
+
end
|
|
79
|
+
begin
|
|
80
|
+
LoadedDie::Sampler.new [[1.0]]
|
|
81
|
+
rescue IndexError
|
|
82
|
+
else
|
|
83
|
+
fail ""
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Sending new with superfluous non-block arguments to LoadedDie::Sampler should
|
|
87
|
+
# raise an exception.
|
|
88
|
+
begin
|
|
89
|
+
LoadedDie::Sampler.new({}, nil)
|
|
90
|
+
rescue ArgumentError
|
|
91
|
+
else
|
|
92
|
+
fail ""
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# A sampler should say it responds to the expected messages.
|
|
96
|
+
class ::Object
|
|
24
97
|
sampler = LoadedDie::Sampler.new({})
|
|
25
|
-
sampler.respond_to?
|
|
26
|
-
sampler.respond_to?
|
|
27
|
-
sampler.respond_to?
|
|
28
|
-
end
|
|
98
|
+
fail "" unless sampler.respond_to? :length
|
|
99
|
+
fail "" unless sampler.respond_to? :[]
|
|
100
|
+
fail "" unless sampler.respond_to? :sample
|
|
101
|
+
end
|
|
29
102
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
103
|
+
# Sampler length should be the sum of weights converted to floats.
|
|
104
|
+
fail "" unless 0.0 == LoadedDie::Sampler.new([]).length
|
|
105
|
+
fail "" unless 2.0 == LoadedDie::Sampler.new([[:a, 2.0]]).length
|
|
106
|
+
fail "" unless (Float(2) + 0.0 + 3.1) ==
|
|
107
|
+
LoadedDie::Sampler.new([[:a, 2], [:b, 0.0], [:c, 3.1]]).length
|
|
108
|
+
|
|
109
|
+
# Sending length with superfluous non-block arguments to a sampler should raise
|
|
110
|
+
# an exception.
|
|
111
|
+
begin
|
|
112
|
+
LoadedDie::Sampler.new({}).length nil
|
|
113
|
+
rescue ArgumentError
|
|
114
|
+
else
|
|
115
|
+
fail ""
|
|
116
|
+
end
|
|
33
117
|
|
|
34
|
-
|
|
118
|
+
# An empty sampler should always find nil when queried.
|
|
119
|
+
class ::Object
|
|
35
120
|
sampler = LoadedDie::Sampler.new({})
|
|
36
|
-
nil
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
:a
|
|
44
|
-
:
|
|
45
|
-
:b
|
|
46
|
-
:b
|
|
47
|
-
:c
|
|
48
|
-
:c
|
|
49
|
-
nil
|
|
50
|
-
nil
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
121
|
+
fail "" unless nil.equal? sampler[0.42]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# A sampler should find the expected segment or nil when queried.
|
|
125
|
+
class ::Object
|
|
126
|
+
sampler = LoadedDie::Sampler.new [[:a, 3], ["!", 0.0], [:b, 2.0], [:c, 5]]
|
|
127
|
+
fail "" unless nil.equal? sampler[-1.0]
|
|
128
|
+
fail "" unless :a.equal? sampler[0.0]
|
|
129
|
+
fail "" unless :a.equal? sampler[2.9]
|
|
130
|
+
fail "" unless :b.equal? sampler[3.0]
|
|
131
|
+
fail "" unless :b.equal? sampler[4.9]
|
|
132
|
+
fail "" unless :c.equal? sampler[5.0]
|
|
133
|
+
fail "" unless :c.equal? sampler[9.9]
|
|
134
|
+
fail "" unless nil.equal? sampler[10.0]
|
|
135
|
+
fail "" unless nil.equal? sampler[11.0]
|
|
136
|
+
fail "" unless nil.equal? sampler[0.0 / 0.0] # NaN
|
|
137
|
+
fail "" unless nil.equal? sampler[1.0 / 0.0] # infinity
|
|
138
|
+
|
|
139
|
+
# This non-float should be converted to a float.
|
|
140
|
+
fail "" unless :b.equal? sampler[4]
|
|
141
|
+
|
|
142
|
+
# An exception should be raised when the argument cannot be converted to a
|
|
143
|
+
# float.
|
|
144
|
+
begin
|
|
145
|
+
sampler[nil]
|
|
146
|
+
rescue TypeError
|
|
147
|
+
else
|
|
148
|
+
fail ""
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Sending [] with superfluous non-block arguments to a sampler should raise an
|
|
153
|
+
# exception.
|
|
154
|
+
begin
|
|
155
|
+
LoadedDie::Sampler.new({})[0.0, nil]
|
|
156
|
+
rescue ArgumentError
|
|
157
|
+
else
|
|
158
|
+
fail ""
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# An empty sampler should return nil when sampled with the default
|
|
162
|
+
# random number generator.
|
|
163
|
+
class ::Object
|
|
54
164
|
sampler = LoadedDie::Sampler.new({})
|
|
55
|
-
nil
|
|
56
|
-
end
|
|
165
|
+
fail "" unless nil.equal? sampler.sample
|
|
166
|
+
end
|
|
57
167
|
|
|
58
|
-
|
|
59
|
-
|
|
168
|
+
# A sampler should return the expected individual when the random number
|
|
169
|
+
# generator is faked and it should return something reasonable when sampled
|
|
170
|
+
# with the default random number generator.
|
|
171
|
+
class ::Object
|
|
172
|
+
sampler = LoadedDie::Sampler.new [[:a, 3], [:b, 2.0], ["!", 0.0], [:c, 9001]]
|
|
60
173
|
rng = Object.new
|
|
61
|
-
def rng.rand
|
|
62
|
-
(3 + 2.0 + 9001) == n
|
|
63
|
-
4.
|
|
174
|
+
def rng.rand n
|
|
175
|
+
fail "" unless ((Float(3) + 2.0) + Float(9001)) == n
|
|
176
|
+
4.9
|
|
64
177
|
end
|
|
65
|
-
:b
|
|
66
|
-
[:a, :b, :c].include?
|
|
67
|
-
|
|
178
|
+
fail "" unless :b.equal? sampler.sample({ :random => rng })
|
|
179
|
+
fail "" unless [:a, :b, :c].include? sampler.sample
|
|
180
|
+
# Unknown options should be ignored.
|
|
181
|
+
fail "" unless :b.equal? sampler.sample({ :random => rng, "?" => "?" })
|
|
182
|
+
fail "" unless [:a, :b, :c].include? sampler.sample({ "?" => "?" })
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Sending sample with superfluous non-block arguments to a sampler should raise
|
|
186
|
+
# an exception.
|
|
187
|
+
begin
|
|
188
|
+
LoadedDie::Sampler.new({}).sample({}, nil)
|
|
189
|
+
rescue ArgumentError
|
|
190
|
+
else
|
|
191
|
+
fail ""
|
|
192
|
+
end
|
|
68
193
|
|
|
69
|
-
|
|
194
|
+
# If this message doesn't get written, there was a problem.
|
|
195
|
+
puts "Test finished: #{__FILE__}"
|
metadata
CHANGED
|
@@ -1,49 +1,72 @@
|
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: loaded_die
|
|
3
|
-
version: !ruby/object:Gem::Version
|
|
4
|
-
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 13
|
|
5
|
+
prerelease:
|
|
6
|
+
segments:
|
|
7
|
+
- 2
|
|
8
|
+
- 0
|
|
9
|
+
- 1
|
|
10
|
+
version: 2.0.1
|
|
5
11
|
platform: ruby
|
|
6
|
-
authors:
|
|
12
|
+
authors:
|
|
7
13
|
- Aaron Beckerman
|
|
8
14
|
autorequire:
|
|
9
15
|
bindir: bin
|
|
10
16
|
cert_chain: []
|
|
11
|
-
|
|
17
|
+
|
|
18
|
+
date: 2025-05-16 00:00:00 -07:00
|
|
19
|
+
default_executable:
|
|
12
20
|
dependencies: []
|
|
13
|
-
|
|
14
|
-
|
|
21
|
+
|
|
22
|
+
description:
|
|
15
23
|
email:
|
|
16
24
|
executables: []
|
|
25
|
+
|
|
17
26
|
extensions: []
|
|
27
|
+
|
|
18
28
|
extra_rdoc_files: []
|
|
19
|
-
|
|
29
|
+
|
|
30
|
+
files:
|
|
20
31
|
- LICENSE.txt
|
|
21
|
-
- README.
|
|
32
|
+
- README.txt
|
|
22
33
|
- lib/loaded_die.rb
|
|
23
|
-
- loaded_die.gemspec
|
|
24
34
|
- test/loaded_die_test.rb
|
|
35
|
+
has_rdoc: true
|
|
25
36
|
homepage:
|
|
26
|
-
licenses:
|
|
37
|
+
licenses:
|
|
27
38
|
- MIT
|
|
28
|
-
metadata: {}
|
|
29
39
|
post_install_message:
|
|
30
40
|
rdoc_options: []
|
|
31
|
-
|
|
41
|
+
|
|
42
|
+
require_paths:
|
|
32
43
|
- lib
|
|
33
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
|
34
|
-
|
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
45
|
+
none: false
|
|
46
|
+
requirements:
|
|
35
47
|
- - ">="
|
|
36
|
-
- !ruby/object:Gem::Version
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
hash: 57
|
|
50
|
+
segments:
|
|
51
|
+
- 1
|
|
52
|
+
- 8
|
|
53
|
+
- 7
|
|
37
54
|
version: 1.8.7
|
|
38
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
|
-
|
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
|
+
none: false
|
|
57
|
+
requirements:
|
|
40
58
|
- - ">="
|
|
41
|
-
- !ruby/object:Gem::Version
|
|
42
|
-
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
hash: 3
|
|
61
|
+
segments:
|
|
62
|
+
- 0
|
|
63
|
+
version: "0"
|
|
43
64
|
requirements: []
|
|
44
|
-
|
|
65
|
+
|
|
66
|
+
rubyforge_project:
|
|
67
|
+
rubygems_version: 1.6.2
|
|
45
68
|
signing_key:
|
|
46
|
-
specification_version:
|
|
69
|
+
specification_version: 3
|
|
47
70
|
summary: A library for choosing randomly where some options are more likely than others.
|
|
48
|
-
test_files:
|
|
49
|
-
|
|
71
|
+
test_files: []
|
|
72
|
+
|
checksums.yaml
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
SHA256:
|
|
3
|
-
metadata.gz: b04718fe3728aa1a6f354e81eed37708842a79fb62b6151f6d9a2823bccd603e
|
|
4
|
-
data.tar.gz: a6993994255411fdcd889aab140e8c8a1cb8453fad9ad44fb972076330409534
|
|
5
|
-
SHA512:
|
|
6
|
-
metadata.gz: 52d5caf5851d300a185600d5a6e00203bebf672913a78ab42ca4430a369b5d690033fd679ccbef1df8abac7bbdd6c63e77d8f04067f4445142caf4b052e0850c
|
|
7
|
-
data.tar.gz: 2d896e03013c765bfe6beec383ffa456e9e4c199573d8b77900d3a7badef92f70d0a90a49e467317b98308b6779246c34e52401aec1d602b7e59e844071ab94b
|
data/README.rdoc
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
= Loaded Die
|
|
2
|
-
|
|
3
|
-
Loaded Die is a Ruby library that makes it easy to randomly choose from a set
|
|
4
|
-
of options where some options are more likely than others.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
== Usage
|
|
8
|
-
|
|
9
|
-
This is the basic pattern: First, you load the library. Then you create an
|
|
10
|
-
instance of LoadedDie::Sampler and send the "sample" message to it as many
|
|
11
|
-
times as you like.
|
|
12
|
-
|
|
13
|
-
Let's say you want to choose randomly from three strings: "A", "B", and "C".
|
|
14
|
-
You want "A" and "B" to be equally likely, and "C" to be twice as likely as one
|
|
15
|
-
of them. That means "A" should be 25% likely, B should be 25% likely, and C
|
|
16
|
-
should be 50% likely.
|
|
17
|
-
|
|
18
|
-
You can create your sampler like this:
|
|
19
|
-
|
|
20
|
-
require 'loaded_die'
|
|
21
|
-
sampler = LoadedDie::Sampler.new('A' => 0.25, 'B' => 0.25, 'C' => 0.5)
|
|
22
|
-
|
|
23
|
-
And then sample from it like this:
|
|
24
|
-
|
|
25
|
-
sampler.sample
|
|
26
|
-
|
|
27
|
-
But you don't have to be fussy about how you represent probabilities. That is,
|
|
28
|
-
you don't need to represent them as numbers between 0 and 1 such that they sum
|
|
29
|
-
up to 1. Their relative values are what matter. So you could also create your
|
|
30
|
-
sampler like this:
|
|
31
|
-
|
|
32
|
-
sampler = LoadedDie::Sampler.new('A' => 1, 'B' => 1, 'C' => 2)
|
|
33
|
-
|
|
34
|
-
You can specify the random number generator that sample uses. To do this,
|
|
35
|
-
supply as the argument a hash with your random number generator under the
|
|
36
|
-
+:random+ key. (This is based on the behavior of Ruby's Array#sample.) Your
|
|
37
|
-
random number generator will be sent a "rand" message with one argument, the
|
|
38
|
-
sum of the weights you specified when creating the sampler. The return value
|
|
39
|
-
must be a number greater than or equal to zero and less than the sum of the
|
|
40
|
-
weights.
|
|
41
|
-
|
|
42
|
-
rng = Object.new
|
|
43
|
-
def rng.rand(n)
|
|
44
|
-
0
|
|
45
|
-
end
|
|
46
|
-
sampler.sample(:random => rng)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
== Author
|
|
50
|
-
|
|
51
|
-
This was written by Aaron Beckerman.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
== Copyright
|
|
55
|
-
|
|
56
|
-
This code is distributed under the MIT License (also known as the Expat
|
|
57
|
-
License). See the LICENSE.txt file for details.
|
data/loaded_die.gemspec
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
-
require "loaded_die"
|
|
5
|
-
|
|
6
|
-
Gem::Specification.new do |s|
|
|
7
|
-
s.name = "loaded_die"
|
|
8
|
-
s.version = LoadedDie::VERSION
|
|
9
|
-
s.author = "Aaron Beckerman"
|
|
10
|
-
s.summary = %q{A library for choosing randomly where some options are more likely than others.}
|
|
11
|
-
s.description = %q{Loaded Die is a library that makes it easy to choose randomly from a set of options where some options are more likely than others.}
|
|
12
|
-
s.license = "MIT"
|
|
13
|
-
s.platform = Gem::Platform::RUBY
|
|
14
|
-
s.required_ruby_version = ">= 1.8.7"
|
|
15
|
-
s.files = ["LICENSE.txt", "README.rdoc", "loaded_die.gemspec", "lib/loaded_die.rb", "test/loaded_die_test.rb"]
|
|
16
|
-
s.test_files = ["test/loaded_die_test.rb"]
|
|
17
|
-
s.executables = []
|
|
18
|
-
s.require_paths = ["lib"]
|
|
19
|
-
end
|