Class: PickMeToo

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

Overview

An “urn” from which you can pick things with specified frequencies.

require 'pick_me_too'

wandering_monsters = PickMeToo.new({goblin: 10, bugbear: 2, orc: 5, spider: 3, troll: 1})
10.times.map { wandering_monsters.pick }
# => [:goblin, :orc, :bugbear, :orc, :goblin, :bugbear, :goblin, :goblin, :orc, :goblin]

irrational = PickMeToo.new({e: Math::E, pi: Math::PI})
to.times.map { irrational.pick }
# => [:e, :e, :e, :pi, :e, :e, :e, :pi, :pi, :e]

Items once picked are “placed back in the urn”, so if you pick a cat this doesn’t reduce the probability the next thing you pick is also a cat, and the urn will never be picked empty. (And of course this is all a metaphor.)

Defined Under Namespace

Classes: Error

Constant Summary collapse

VERSION =
'1.1.1'

Instance Method Summary collapse

Constructor Details

#initialize(frequencies, rnd = -> { rand }) ⇒ PickMeToo

“Fill” the urn.

The required frequencies parameter must be something that is effectively a list of pairs: things to pick paired with their frequency. The “frequency” is just any positive number.

The optional rnd parameter is a Proc that when called returns a number, ideally in the interval [0, 1]. This parameter allows you to provided a seeded random number generator, so the choices occur in a predictable sequence, which is useful for testing.

This constructor method will raise a ‘PickMeToo::Error` if

  • there are no pairs in the frequency list

  • any of the frequencies is non-positive

  • any of the items in the list isn’t something followed by a number



38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/pick_me_too.rb', line 38

def initialize(frequencies, rnd = -> { rand })
  @rnd = rnd
  frequencies = prepare(Array(frequencies))
  @objects = frequencies.map(&:first)
  if @objects.length == 1
    @picker = ->(_p) { 0 }
  else
    root = balanced_binary_tree(frequencies)
    # compile everything into a nested ternary expression
    @picker = eval "->(p) { #{ternarize(root, 0)} }"
  end
end

Instance Method Details

#pickObject

Pick an item from the urn.



53
54
55
# File 'lib/pick_me_too.rb', line 53

def pick
  @objects[@picker.call(@rnd.call)]
end

#randomize!(rnd = -> { rand }) ⇒ Object

Replace the random number generator.

If the optional argument is omitted, the replacement is just

-> { rand }

This is useful if you want to switch from a seeded random number generator to something more truly random.



66
67
68
# File 'lib/pick_me_too.rb', line 66

def randomize!(rnd = -> { rand })
  @rnd = rnd
end