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?