| Path: | lib/charlie/mutate.rb |
| Last Update: | Sat Jan 26 00:46:58 +0100 2008 |
Takes mutator m1 with probability p, and mutator m2 with probability 1-p
# File lib/charlie/mutate.rb, line 10
10: def PMutate(p,m1,m2=NullMutator)
11: raise ArgumentError, "first argument to PMutate should be numeric (probability)." unless p.is_a?(Numeric)
12: return m1 if m1==m2
13: m1_name, m2_name = [m1,m2].map{|c| '_mutate_' + c.to_s.gsub(/[^A-Za-z0-9]/,'') + '!' }
14: Module.new{
15: include m1.dup # dup to avoid bugs on use PMutate(..,m1) .. use m1
16: alias_method m1_name, :mutate!
17: include m2.dup
18: alias_method m2_name, :mutate!
19:
20: define_method(:mutate!) {
21: rand < p ? send(m1_name) : send(m2_name)
22: }
23: self.name= "PMutate(#{p},#{m1},#{m2})"
24: }
25: end
Variant of PMutate for more than 2 mutators
# File lib/charlie/mutate.rb, line 31
31: def PMutateN(hash)
32: tot_p = hash.inject(0){|s,(m,p)| s+p }
33: if (tot_p - 1.0).abs > 0.01 # close to 1?
34: raise ArgumentError, "PMutateN: sum of probabilities > 1.0" if tot_p > 1.0
35: hash[NullMutator] = (hash[NullMutator] || 0.0) + (1.0 - tot_p)
36: end
37: partial_sums = hash.sort_by{|m,p| -p } # max probability first
38: s = 0.0
39: partial_sums.map!{|m,p| ['_mutate_' + m.to_s.gsub(/[^A-Za-z0-9]/,'') + '!' , s+=p, m] }
40:
41: Module.new{
42: partial_sums.each{|name,p,mod|
43: include mod.dup
44: alias_method name, :mutate!
45: }
46: define_method(:mutate!) {
47: r = rand
48: send partial_sums.find{|name,p,mod| p >= r }.first
49: }
50: self.name= "PMutateN(#{hash.inspect})"
51: }
52: end