Da due giorni ormai mi sto rompendo la testa per far funzionare tutto in ruby, ma ottengo comportamenti sempre più imprevedibili... Questo dovrebbe essere un algoritmo genetico per evolvere delle funzioni che approssimino relazioni tra ennuple di valori e una loro immagine.
In sostanza, dati a, b, c, d, ..., y approssima f tale che f(a, b, c, d, ...) = y:
codice:
#....
class GeneticEngine
attr_accessor :population_size, :crossover_number, :mutation_probability, :max_depth, :elitism
attr_accessor :values #array di array
attr_reader :population
def initialize()
end
def current_generation()
return @current_generation
end
def generate_population()
@population = Array.new()
for i in (1..@population_size) do
@population[i - 1] = Boid.create_random(@max_depth)
@population[i - 1].evaluate_fitness(values)
end
@current_generation = 0
end
def next_generation()
@population.sort! { |x, y| x.fitness <=> y.fitness }
best = @population[0..(@crossover_number-1)]
0.upto(@crossover_number - 1) do |i|
@population.concat(Boid.crossover(@population[i * 2], @population[i * 2 + 1]))
end
@population.each { |boid| boid.evaluate_fitness(@values) }
#@population.each { |boid| puts boid.fitness }
#gets
tmp_pop = Array.new()
0.upto(@population_size - 1) do |i|
if @population[i].fitness >= Float::MAX then
tmp_pop[i] = nil
else
tmp_pop[i] = @population[i]
end
end
tmp_pop.compact! #elimina nil
tmp_pop.sort! { |x, y| x.fitness <=> y.fitness }
#tmp_pop.each { |boid| puts boid.fitness }
#gets
if tmp_pop.length > @population_size then
tmp_pop = tmp_pop[0..(@population_size-1)]
end
tmp_pop.each { |boid| boid.attempt_mutation(@mutation_probability) }
if tmp_pop.length < @population_size then
(tmp_pop.length).upto(@population_size - 1) do |i|
tmp_pop[i] = Boid.create_random(@max_depth)
end
end
tmp_pop.each { |boid| boid.evaluate_fitness(@values) }
minfit = (tmp_pop.min { |x, y| x.fitness <=> y.fitness }).fitness
bestfit = (best.min { |x, y| x.fitness <=> y.fitness}).fitness
if bestfit < minfit then
tmp_pop[-(@crossover_number)..-1] = best
end
@population = tmp_pop
@current_generation += 1
end
def get_best_solution()
return (@population.sort { |x, y| x.fitness <=> y.fitness })[0]
end
end
g = GeneticEngine.new()
g.population_size = 20
g.crossover_number = 5
g.mutation_probability = 0.7
g.max_depth = 1
g.values = [[1, 2], [2, 3], [3, 4]]
g.generate_population()
while (g.current_generation < 1450) do
g.next_generation()
#g.population.each { |x| puts "> " + x.expression.to_s(), " > " + x.fitness.to_s() }
best = g.get_best_solution()
if best then
if best.fitness == 0 then
break
end
if g.current_generation % 50 == 0 then
puts "Generation " + g.current_generation.to_s()
puts "Best solution: " + best.expression.to_s()
puts "Fitness: " + best.fitness.to_s()
end
end
end
Il problema è che nelle 1450 generazioni la soluzione migliore è sempre la stessa, con una fitness orribile. Dopo vari studi, ho notato che compaiono soluzioni con fitness molto bassa, ma, non so perchè, scompaiono dopo poco tempo. Per evitare questo ho anche forzato l'inserimento delle migliori soluzioni nell'array finale:
codice:
tmp_pop.each { |boid| boid.evaluate_fitness(@values) }
minfit = (tmp_pop.min { |x, y| x.fitness <=> y.fitness }).fitness
bestfit = (best.min { |x, y| x.fitness <=> y.fitness}).fitness
if bestfit < minfit then
tmp_pop[-(@crossover_number)..-1] = best
end
Ma anche così le soluzioni con fitness bassa continuano a scomparire, e rimane sempre una funzione stupida che si impone sulle altre... Fino a non poco tempo fa, risultava addirittura che questa funzione rimpiazzasse tutte le altre (non si sa come). Qualcuno mi sa aiutare?