loaded_die 1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 362b5a5dc03dcbf3580aeb5501c964cb273a183ce9ab3763bc9840288f4e4b95
4
+ data.tar.gz: e5e901d9a0611c2579d33595569658d112736f89714655db4b161349c5f00d24
5
+ SHA512:
6
+ metadata.gz: 5510423a7c21354514d3a035f77b7c518d2ed563ac315dc1d1ebba6f10459201416a5ed427cadb9c78c957a94a591a9766c75dabe8b15ca96911e8aed413350b
7
+ data.tar.gz: 5d8ccf6c0f9713cf6e1d3bf1b45ba63d7c026d7e41523e0c61211163815173cdeff20341b5c4518350674ecc0f67565de16f29dd1f063a7dc2d347e9084c01f7
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2018 Aaron Beckerman
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,57 @@
1
+ = Loaded Die
2
+
3
+ Loaded Die is a Ruby library that makes it easy to randomly choose from a set
4
+ of options when 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 the "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.
@@ -0,0 +1,52 @@
1
+ module LoadedDie
2
+ VERSION = '1.0.1'
3
+
4
+ class Sampler
5
+ DEFAULT_RNG = ::Object.new
6
+ def DEFAULT_RNG.rand(n)
7
+ ::Kernel.rand * n
8
+ end
9
+
10
+ Segment = ::Struct.new(:maximum, :individual)
11
+
12
+ def initialize(population)
13
+ @compiled = population.inject [] do |accum, (individual, weight)|
14
+ if weight <= 0
15
+ raise ::ArgumentError, "non-positive weight #{weight}"
16
+ end
17
+ prev_max =
18
+ if last = accum.last
19
+ last.maximum
20
+ else
21
+ 0
22
+ end
23
+ accum << Segment.new(prev_max + weight, individual)
24
+ end
25
+ nil
26
+ end
27
+
28
+ def length
29
+ if last = @compiled.last
30
+ last.maximum
31
+ else
32
+ 0
33
+ end
34
+ end
35
+
36
+ def [](point)
37
+ if point < 0
38
+ nil
39
+ elsif choice = @compiled.detect { |segment| point < segment.maximum }
40
+ choice.individual
41
+ else
42
+ nil
43
+ end
44
+ end
45
+
46
+ def sample(options = {})
47
+ rng = options.fetch(:random) { DEFAULT_RNG }
48
+ point = rng.rand(length)
49
+ self[point]
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,19 @@
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 when some options are more likely than others.}
11
+ s.description = %q{Loaded Die makes it easy to choose randomly when 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
@@ -0,0 +1,71 @@
1
+ require 'loaded_die'
2
+
3
+ String == LoadedDie::VERSION.class or fail
4
+
5
+ Module == LoadedDie.class or fail
6
+
7
+ Class == LoadedDie::Sampler.class or fail
8
+
9
+ begin
10
+ LoadedDie::Sampler.new(:a => 0)
11
+ rescue ArgumentError
12
+ else
13
+ fail
14
+ end
15
+
16
+ begin
17
+ LoadedDie::Sampler.new(:a => -1)
18
+ rescue ArgumentError
19
+ else
20
+ fail
21
+ end
22
+
23
+ lambda do
24
+ sampler = LoadedDie::Sampler.new({})
25
+ sampler.respond_to?(:length) or fail
26
+ sampler.respond_to?(:[]) or fail
27
+ sampler.respond_to?(:sample) or fail
28
+ end.call
29
+
30
+ lambda do
31
+ 0 == LoadedDie::Sampler.new({}).length or fail
32
+ 2 == LoadedDie::Sampler.new({ :a => 2 }).length or fail
33
+ (2 + 3.1) == LoadedDie::Sampler.new({ :a => 2, :b => 3.1 }).length or fail
34
+ end.call
35
+
36
+ lambda do
37
+ sampler = LoadedDie::Sampler.new({})
38
+ nil == sampler[0.42] or fail
39
+ end.call
40
+
41
+ lambda do
42
+ sampler = LoadedDie::Sampler.new([[:a, 3], [:b, 2.0], [:c, 5]])
43
+ nil == sampler[-1.0] or fail
44
+ :a == sampler[0.0] or fail
45
+ :a == sampler[2.99] or fail
46
+ :b == sampler[3] or fail
47
+ :b == sampler[3.0] or fail
48
+ :b == sampler[4.99] or fail
49
+ :c == sampler[5.0] or fail
50
+ :c == sampler[9.99] or fail
51
+ nil == sampler[10.0] or fail
52
+ nil == sampler[11.0] or fail
53
+ end.call
54
+
55
+ lambda do
56
+ sampler = LoadedDie::Sampler.new({})
57
+ nil == sampler.sample or fail
58
+ end.call
59
+
60
+ lambda do
61
+ sampler = LoadedDie::Sampler.new([[:a, 3], [:b, 2.0], [:c, 9001]])
62
+ rng = Object.new
63
+ def rng.rand(n)
64
+ (3 + 2.0 + 9001) == n or fail
65
+ 4.99
66
+ end
67
+ :b == sampler.sample(:random => rng) or fail
68
+ [:a, :b, :c].include?(sampler.sample) or fail
69
+ end.call
70
+
71
+ puts 'Test finished.'
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: loaded_die
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Beckerman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-05-31 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Loaded Die makes it easy to choose randomly when some options are more
14
+ likely than others.
15
+ email:
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - LICENSE.txt
21
+ - README.rdoc
22
+ - lib/loaded_die.rb
23
+ - loaded_die.gemspec
24
+ - test/loaded_die_test.rb
25
+ homepage:
26
+ licenses:
27
+ - MIT
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: 1.8.7
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 2.7.6
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: A library for choosing randomly when some options are more likely than others.
49
+ test_files:
50
+ - test/loaded_die_test.rb