#!/usr/local/bin/ruby RIGHT_ANSWERS = [3, 1, 2, 2, 2, 3, 1, 3, 3, 1] # 遺伝子クラス class Gene attr_accessor :answers attr_reader :generation, :point def initialize @answers = set_answers @generation = 1 calc_point end # Gene.dup した際の挙動 def initialize_copy(obj) @answers = obj.answers.dup @generation = obj.generation + 1 # calc_point # mutate 後に再計算するので不要 end # 回答をランダムに組み合わせる def set_answers array = [] 10.times { array << rand(3) + 1 } array end # 得点を計算 def calc_point @point = 0 10.times { |i| @point += 10 if @answers[i] == RIGHT_ANSWERS[i] } end def show puts "#{@answers}: #{@point} (#{@generation})" end # 突然変異 - 10 問中のいずれか 1 つの回答を 1 進める def mutate mutate_at = rand(10) answer = @answers[mutate_at] @answers[mutate_at] = answer == 3 ? 1 : answer + 1 end end # 遺伝子の集合を操作するクラス class GA attr_accessor :genes attr_reader :round def initialize(num = 10) set_genes(num) @round = 0 end def show_all @genes.each { |gene| gene.show } end # 遺伝子を指定数だけ用意する def set_genes(num) @genes = [] num.times { @genes << Gene.new } @genes end # 成績に基づく淘汰 (入れ替え) def shakeout(num = 2) @round += 1 puts "### Shakeout round #{@round} ###" # puts "#{RIGHT_ANSWERS} <= RIGHT ANSWERS" # 点数を目検する際に表示 #@genes.sort! { |m, n| n.point m.point } # 得点降順で並び替え @genes.sort { |m, n| n.point m.point } # 得点降順で並び替え top_genes = @genes[0, num] child_genes = breed(top_genes) num.times { @genes.pop } @genes = (child_genes + @genes).sort! { |m, n| n.point m.point } end # 交配 def breed(top_genes) breed_at = rand(9) + 1 # 先頭からの交叉はなしとする? puts "Breed top #{top_genes.size} genes at [#{breed_at}]:" top_genes.each { |gene| gene.show } tmp = [] results = [] # 指定位置以降の回答を交叉する top_genes.each do |gene| child = gene.dup answer_size = child.answers.size child.answers.concat(tmp) tmp = child.answers.slice!(breed_at..answer_size - 1) results << child end results[0].answers.concat(tmp) results.each do |child| child.mutate child.calc_point end puts "Children are:" results.each { |child| child.show } puts "\n" results end end ga = GA.new(10) while ga.genes[0].point < 100 ga.show_all # puts "press ENTER to run the next shakeout" # gets.chomp ga.shakeout end ga.show_all puts "Reached 100 at round #{ga.round}"