weighted_distribution 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []