manis
manis

Reputation: 729

Choosing random object in array based on percentages

Say I have a method that takes an array of objects, each with a percentage attribute controlling how often the element is returned by the method

Ie. I want to be able to add an object to an array E, with percentage attribute 0.20 and have it returned approximately every fifth method call. What would be the best way to implement such a method?

PS. Not just have an array of 5 objects and select one at random

Upvotes: 2

Views: 907

Answers (1)

Robert Krzyzanowski
Robert Krzyzanowski

Reputation: 9344

Maybe something like this?

class Sample

  def initialize(e)
    @E = e
    @sums = []
    @E.each_with_index { |x, i| @sums << (@sums.last || 0) + @E[i][:pct] }
  end

  def draw
    rand = Random.rand()

    for i in 0..(@sums.length-1)
      return @E[i][:el] if rand <= @sums[i]
    end
  end

end

Example

ss = Sample.new(e = [{el: "hello", pct: 0.3}, {el: "world", pct: 0.3}, {el: "goodbye", pct: 0.4}]) 
# Draw "hello" with 30%, "world" with 30%, "goodbye" with 40% probability, respectively   
results = []
10_000.times { results << ss.draw }
e.map { |x| { x[:el] => results.count { |y| y == x[:el] }.to_f / results.length } }.reduce(:merge)
# observed percentages of 10,000 draws
# {"hello"=>0.2938, "world"=>0.3046, "goodbye"=>0.4016}

Upvotes: 3

Related Questions