Weighted random methods for Array
1 2 class Array 3 # Chooses a random array element from the receiver based on the weights 4 # provided. If _weights_ is nil, then each element is weighed equally. 5 # 6 # [1,2,3].random #=> 2 7 # [1,2,3].random #=> 1 8 # [1,2,3].random #=> 3 9 # 10 # If _weights_ is an array, then each element of the receiver gets its 11 # weight from the corresponding element of _weights_. Notice that it 12 # favors the element with the highest weight. 13 # 14 # [1,2,3].random([1,4,1]) #=> 2 15 # [1,2,3].random([1,4,1]) #=> 1 16 # [1,2,3].random([1,4,1]) #=> 2 17 # [1,2,3].random([1,4,1]) #=> 2 18 # [1,2,3].random([1,4,1]) #=> 3 19 # 20 # If _weights_ is a symbol, the weight array is constructed by calling 21 # the appropriate method on each array element in turn. Notice that 22 # it favors the longer word when using :length. 23 # 24 # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "hippopotamus" 25 # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "dog" 26 # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "hippopotamus" 27 # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "hippopotamus" 28 # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "cat" 29 def random(weights=nil) 30 return random(map {|n| n.send(weights)}) if weights.is_a? Symbol 31 32 weights ||= Array.new(length, 1.0) 33 total = weights.inject(0.0) {|t,w| t+w} 34 point = rand * total 35 36 zip(weights).each do |n,w| 37 return n if w >= point 38 point -= w 39 end 40 end 41 42 # Generates a permutation of the receiver based on _weights_ as in 43 # Array#random. Notice that it favors the element with the highest 44 # weight. 45 # 46 # [1,2,3].randomize #=> [2,1,3] 47 # [1,2,3].randomize #=> [1,3,2] 48 # [1,2,3].randomize([1,4,1]) #=> [2,1,3] 49 # [1,2,3].randomize([1,4,1]) #=> [2,3,1] 50 # [1,2,3].randomize([1,4,1]) #=> [1,2,3] 51 # [1,2,3].randomize([1,4,1]) #=> [2,3,1] 52 # [1,2,3].randomize([1,4,1]) #=> [3,2,1] 53 # [1,2,3].randomize([1,4,1]) #=> [2,1,3] 54 def randomize(weights=nil) 55 return randomize(map {|n| n.send(weights)}) if weights.is_a? Symbol 56 57 weights = weights.nil? ? Array.new(length, 1.0) : weights.dup 58 59 # pick out elements until there are none left 60 list, result = self.dup, [] 61 until list.empty? 62 # pick an element 63 result << list.random(weights) 64 # remove the element from the temporary list and its weight 65 weights.delete_at(list.index(result.last)) 66 list.delete result.last 67 end 68 69 result 70 end 71 end
Intended for use in selecting random elements from an array based on weight. Not optimized!