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.
Files changed (2) hide show
  1. data/lib/weighted_distribution.rb +81 -0
  2. 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: []