weighted_distribution 1.0.0
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/lib/weighted_distribution.rb +81 -0
- metadata +78 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
# Implements randomization of weighted objects.
|
2
|
+
# Constructor expects a hash of object => weight pairs where the
|
3
|
+
# weight represents the probability of the corresponding
|
4
|
+
# object being selected. follows specification of ryanlecompte's
|
5
|
+
# WeightedDistributionizer[link:https://github.com/ryanlecompte/weighted_randomizer]
|
6
|
+
# reimplements the sample function to run in O(log(n)) rather than O(n).
|
7
|
+
#
|
8
|
+
# @example Usage
|
9
|
+
# suit_dist = WeightedDistribution.new({heart: 12, spade: 11, diamond: 10, club:13})
|
10
|
+
# puts "you picked a #{suit_dist.sample}"
|
11
|
+
#
|
12
|
+
# @note Mostly adapted from recipe 5.11 from the Ruby Cookbook.
|
13
|
+
class WeightedDistribution
|
14
|
+
|
15
|
+
# Creates a new instance of the WeightedDistributionizer Class
|
16
|
+
#
|
17
|
+
# @param [Hash] object_weights objects with their corresponding
|
18
|
+
# weights(key object, value weight)
|
19
|
+
# @return [WeightedDistributionizer]
|
20
|
+
def initialize(object_weights)
|
21
|
+
@len = object_weights.length
|
22
|
+
@keys = object_weights.keys
|
23
|
+
@orig_values = @keys.map{|x| object_weights[x]}
|
24
|
+
@values = normalize(@orig_values)
|
25
|
+
@csum = cumulative_sum(@values)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Samples from the WeightedDistribution. Each object has a
|
29
|
+
# probability of being selected equal to its weight over the
|
30
|
+
# total sum of all the object weights. Can specify a number of samples
|
31
|
+
# to obtain from distribution with the num argument.
|
32
|
+
#
|
33
|
+
# @param [Integer] num the number of samples to return (optional)
|
34
|
+
# @return [Object, Array<Object>] one or more sampled objects.
|
35
|
+
def sample(num = nil)
|
36
|
+
return _sample unless num
|
37
|
+
num = 0 if num < 0
|
38
|
+
Array.new(num) { _sample }
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# Returns a single sample from the WeightedDistribution. Preforms altered binary
|
44
|
+
# search in order to find the corresponding object in O(log(n)) time.
|
45
|
+
#
|
46
|
+
# @return [Object] sampled object from distribution
|
47
|
+
def _sample
|
48
|
+
random = Kernel.rand
|
49
|
+
lhs, rhs = 0, @len - 1
|
50
|
+
|
51
|
+
while rhs >= lhs
|
52
|
+
ptr = (lhs + rhs) / 2
|
53
|
+
if @csum[ptr] < random
|
54
|
+
lhs = ptr + 1
|
55
|
+
elsif @csum[ptr] - @values[ptr] > random
|
56
|
+
rhs = ptr - 1
|
57
|
+
else
|
58
|
+
return @keys[ptr]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
# Computes the CDF of the weighted distribution which will be sorted
|
66
|
+
# to facilitate the binary search.
|
67
|
+
#
|
68
|
+
# @param [Array<Float>] object_distribution normalized weights
|
69
|
+
# @return [Array<Float>] cumulative distribution of weights
|
70
|
+
def cumulative_sum(object_distribution)
|
71
|
+
sum = 0
|
72
|
+
object_distribution.map{|x| sum += x}
|
73
|
+
end
|
74
|
+
|
75
|
+
# Normalizes the weights to create probability distribution that sums
|
76
|
+
# to 1.0
|
77
|
+
def normalize(array)
|
78
|
+
sum = array.reduce(:+)
|
79
|
+
array.map{|x| x.to_f/sum}
|
80
|
+
end
|
81
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: weighted_distribution
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jonathan Swenson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-04-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
version_requirements: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
none: false
|
22
|
+
requirement: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
none: false
|
28
|
+
prerelease: false
|
29
|
+
type: :development
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
version_requirements: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
none: false
|
38
|
+
requirement: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
none: false
|
44
|
+
prerelease: false
|
45
|
+
type: :development
|
46
|
+
description: Sample a weighted distribution of objects.
|
47
|
+
email: jonathanswensonsc@gmail.com
|
48
|
+
executables: []
|
49
|
+
extensions: []
|
50
|
+
extra_rdoc_files: []
|
51
|
+
files:
|
52
|
+
- lib/weighted_distribution.rb
|
53
|
+
homepage: http://github.com/jonathanswensonsc/weighted_distribution
|
54
|
+
licenses:
|
55
|
+
- MIT
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
none: false
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
none: false
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.8.24
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: Sample a weighted distribution of objects.
|
78
|
+
test_files: []
|