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.
- checksums.yaml +7 -0
- data/LICENSE.txt +19 -0
- data/README.rdoc +57 -0
- data/lib/loaded_die.rb +52 -0
- data/loaded_die.gemspec +19 -0
- data/test/loaded_die_test.rb +71 -0
- metadata +50 -0
checksums.yaml
ADDED
|
@@ -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
|
data/LICENSE.txt
ADDED
|
@@ -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.
|
data/README.rdoc
ADDED
|
@@ -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.
|
data/lib/loaded_die.rb
ADDED
|
@@ -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
|
data/loaded_die.gemspec
ADDED
|
@@ -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
|