class RandomItemSelector def initialize(items, weightFunc) @weightFunc = weightFunc @items = items @totalWeight = nil end def next target = rand(totalWeight) min = 0 @items.each { |item| max = min + @weightFunc.call(item) if min <= target && target < max return item; end min = max } raise "Internal error: can't select random item" end def itemsChanged @totalWeight = nil end def totalWeight if @totalWeight.nil? @totalWeight = 0 @items.each { |item| @totalWeight += @weightFunc.call(item) } end return @totalWeight end end Usage: items = [] for i in 1..100 items.push case rand(3) when 0 'Red' when 1 'Green' when 2 'Blue' end end selector = RandomItemSelector.new(items, proc { |item| case item when 'Red' 10 when 'Green' 5 when 'Blue' 1 end }) for i in 1..20 print selector.next print " " end