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

Benjamin Mako Hill || Want to submit a patch?