Class: LoadedDie::Sampler

Inherits:
Object
  • Object
show all
Defined in:
lib/loaded_die.rb

Overview

Instances of this class can choose randomly from a set of options (called individuals here). The options can have different probabilities of being chosen.

Constant Summary collapse

DEFAULT_RNG =

The default random number generator. It samples from a uniform distribution.

::Object.new

Instance Method Summary collapse

Constructor Details

#initialize(population) ⇒ Sampler

Creates a sampler from a population. The argument should return an enumerable (conventionally an instance of Enumerator) of arrays when sent a to_enum message with no arguments. (A normal instance of Hash qualifies.) Each array must have at least two elements: the first element is an individual that can be chosen and the second element is its weight – that is, the likelihood relative to the other weights that the individual will be chosen. Weights must convert to finite floats that are zero or positive.



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/loaded_die.rb', line 33

def initialize population
  @compiled = population.to_enum.inject [] do |accum, elem|
    individual = elem.fetch 0
    weight = elem.fetch 1
    weight_f = ::Kernel.Float weight
    unless 0.0 <= weight_f && weight_f.finite?
      ::Kernel.raise ::ArgumentError, "invalid weight #{weight_f}"
      break
    end
    prev_max =
      if last = accum.last
        last.fetch :maximum
      else
        0.0
      end
    accum << { :maximum => prev_max + weight_f, :individual => individual }
  end
  nil
end

Instance Method Details

#[](point) ⇒ Object

Returns the individual associated with the given point on a line. The point should convert to a float. If it is greater than or equal to zero and less than the sum of weights, this returns the corresponding individual. If it is outside those bounds, this returns nil.



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/loaded_die.rb', line 70

def [] point
  point_f = ::Kernel.Float point
  if 0.0 > point_f
    nil
  elsif choice = @compiled.detect { |segment|
    segment.fetch(:maximum) > point_f }
    choice.fetch :individual
  else
    nil
  end
end

#lengthObject

Returns the sum of weights.



56
57
58
59
60
61
62
# File 'lib/loaded_die.rb', line 56

def length
  if last = @compiled.last
    last.fetch :maximum
  else
    0.0
  end
end

#sample(options = {}) ⇒ Object

Returns a randomly-chosen individual. The argument is a hash, empty by default. If it contains a value for the :random key, that value will be used as the random number generator instead of the default. This RNG will be sent a rand message with one argument, the sum of weights, and should return a number (convertible to a float) greater than or equal to zero and less than the sum of weights.



90
91
92
93
94
# File 'lib/loaded_die.rb', line 90

def sample options = {}
  rng = options.fetch(:random) { ::LoadedDie::Sampler::DEFAULT_RNG }
  point = rng.rand length
  self[point]
end