Skip to content

Instantly share code, notes, and snippets.

@migimunz
Created September 13, 2014 00:38
Show Gist options
  • Save migimunz/3eb99184fb9d3b8ac5a1 to your computer and use it in GitHub Desktop.
Save migimunz/3eb99184fb9d3b8ac5a1 to your computer and use it in GitHub Desktop.
Solving fizzbuzz with genetic algorithms - somewhat correct!
# The best result I got so far is 72.9% , which is
# close enough for most real word uses.
Defs = {
fizz: 0,
buzz: 1,
fizzbuzz: 2,
neither: 3
}
GENOME_LENGTH = 20
class TrueClass
def to_i
1
end
end
class FalseClass
def to_i
0
end
end
class Array
def swap(i, j)
self[j], self[i] = self[i], self[j]
self
end
end
def random_genome
Array.new(GENOME_LENGTH).map { |e| Defs.values.sample }
end
def fitness(genome)
genome.each_with_index.map do |e, i|
n = i + 1
case
when n % 5 == 0 && n % 3 == 0
(e == Defs[:fizzbuzz]).to_i
when n % 3 == 0
(e == Defs[:fizz]).to_i
when n % 5 == 0
(e == Defs[:buzz]).to_i
else
(e == Defs[:neither]).to_i
end
end.reduce(:+) / genome.length.to_f
end
def select(population, rate = 0.50)
n = (rate * population.size).round
# Stupid, truncation based selection.
raise RuntimeError, "U dun fucked up" if n > population.size
population = population.sort { |a, b| fitness(b) <=> fitness(a) }.slice(0, n)
puts "Current top fitness: #{fitness(population[0])}"
# screw it up a bit, just to avoid local optimum
(n/10).times { population.swap((rand * population.size - 1), (rand * population.size - 1)) }
population
end
def single_point_crossover(a, b)
n = ((a.size-1) * rand).round
[a.slice(0, n+1) + b.slice(n, b.size),
b.slice(0, n+1) + a.slice(n, a.size)]
end
def crossover(selected, size)
children = []
while children.size != size
children.concat(single_point_crossover(selected.sample, selected.sample))
end
children
end
def mutation(population, rate = 0.01)
population.map do |genome|
genome.map do |e|
if rand < rate
Defs.values.sample
else
e
end
end
end
end
def find_solution(population)
population.find { |genome| fitness(genome) == 1 }
end
def evolve_fizz_buzz(generation_count, population_count, crossover_rate, mutation_rate)
population = Array.new(population_count).map { |e| random_genome }
generation_count.times do |gen|
puts "Generation #{gen}"
population = mutation(crossover(select(population, crossover_rate), population_count), mutation_rate)
solution = find_solution(population)
return solution if solution
end
nil
end
puts evolve_fizz_buzz(1000, 1000, 0.2, 0.01)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment