Nullpointerexception - Genetic Algorithm

Asked

Viewed 186 times

-3

package chapter2;

public class AllOnesGA {

    public static void main(String[] args) {
        //Cria instância da classe "GeneticAlgorithm"
        GeneticAlgorithm ga = new GeneticAlgorithm(100, 0.01, 0.95, 0);

        //Inicializa a população
        Population population = new Population(50);

        ga.evalPopulation(population);
        int generation = 1;

        while (ga.isTerminationConditionMet(population) == false) {
            //Mostra o fittest indivíduo da população
            System.out.println("Best solution: " + population.getFittest(0).toString());

            //Aplicar crossover
            population = ga.crossoverPopulation(population);

            //Aplicar mutação
            population = ga.mutatePopualtion(population);

            //Avaliar a população
            ga.evalPopulation(population);

            //Incrementa mais uma geração
            generation++;
        }

        System.out.println("Found solution in " + generation + " generations");
        System.out.println("Best solution: " + population.getFittest(0).toString());
    }
}
//Nova Classe

public class GeneticAlgorithm {
    private int populationSize;
    private double crossoverRate;
    private double mutationRate;
    private int elitismCount;

    public GeneticAlgorithm (int populationSize, double crossoverRate, 
    double mutationRate, int elitismCount) {
        this.populationSize = populationSize;
        this.crossoverRate = crossoverRate;
        this.mutationRate = mutationRate;
        this.elitismCount = elitismCount;
    }

    public Population initPopulation(int chromosomeLenght) {
        Population population = new Population(this.populationSize, chromosomeLenght);
        return population;
    }

    public double calcFitness (Individual individual) {
        //Rastreia o número de genes corretos
        int correctGenes = 0;

        //Loop sobre cada gene
        for (int geneIndex = 0; geneIndex < individual.getChromosomeLenght(); geneIndex++) {
            //Adiciona um ponto de fitness para cada "1" encontrado
            if (individual.getGene(geneIndex) == 1) {
                correctGenes += 1;
            }
        }
        //Calculo de fitness
        double fitness = (double) correctGenes / individual.getChromosomeLenght();

        //Armazena fitness
        individual.setFitness(fitness);

        return fitness;
    }

    public void evalPopulation(Population population) {
        double populationFitness = 0;

        for (Individual individual : population.getIndividuals()) {
            populationFitness += calcFitness(individual);
        }

        population.setPopulationFitness(populationFitness);
    }

    public boolean isTerminationConditionMet(Population population) {
        for (Individual individual : population.getIndividuals()) {
            if (individual.getFitness() == 1) {
                return true;
            }
        }
        return false;
    }

    public Individual selectParent(Population population) {
        //Crossover aplicado com o método "Roleta de Cassino"
        //Seleciona os indivíduos
        Individual individuals[] = population.getIndividuals();

        //Gira a roleta
        double populationFitness = population.getPopulationFitness();
        double rouletteWheelPosition = Math.random() * populationFitness;

        //Encontra o indivíduo para o crossover
        double spinWheel = 0;
        for (Individual individual : individuals) {
            spinWheel += individual.getFitness();
            if (spinWheel >= rouletteWheelPosition) {
                return individual;
            }
        }

        return individuals[population.size() - 1];       
    }

    public Population crossoverPopulation(Population population) {
        //Cria nova população
        Population newPopulation = new Population(population.size());

        //Loop sobre população atual por fitness
        for (int populationIndex = 0; populationIndex < population.size(); populationIndex++) {
            Individual parent1 = population.getFittest(populationIndex);

            //Aplicar crossover a este indivíduo??
            if (this.crossoverRate > Math.random() && populationIndex > this.elitismCount) {
                //Inicializa offspring
                Individual offspring = new Individual(parent1.getChromosomeLenght());

                //Encontra o 2 segundo par
                Individual parent2 = selectParent(population);

                //Loop sobre o genoma
                for (int geneIndex = 0; geneIndex < parent1.getChromosomeLenght(); geneIndex ++) {
                    //Usa metade dos genes do par 1 e metade dos genes do par 2
                    if (0.5 > Math.random()) {
                        offspring.setGene(geneIndex, parent1.getGene(geneIndex));
                    } else {
                        offspring.setGene(geneIndex, parent2.getGene(geneIndex));
                    }
                }
                //Adiciona offspring a nova população
                newPopulation.setIndividual(populationIndex, offspring);
            } else {
                //Adiciona individuo a nova opulação sem aplicar crossover
                newPopulation.setIndividual(populationIndex, parent1);
            }
        }
        return newPopulation;
    }

    public Population mutatePopualtion(Population population) {
        //Inicializa nova População
        Population newPopulation = new Population(this.populationSize);

        //Loop sobre população atual por fitness
        for (int populationIndex = 0; populationIndex < population.size(); populationIndex++) {
            Individual individual = population.getFittest(populationIndex);

            //Loop sobre cada gene
            for (int geneIndex = 0; geneIndex < individual.getChromosomeLenght(); geneIndex++) {
                //Pula mutação se o indivíduo for elite
                if (populationIndex >= this.elitismCount) {
                    //Esse gene precisa de mutação??
                    if (this.mutationRate > Math.random()) {
                        //Pega novo gene
                        int newGene = 1;
                        if (individual.getGene(geneIndex) == 1) {
                            newGene = 0;
                        }
                        //Mutar o gene
                        individual.setGene(geneIndex, newGene);
                    }
                }
            }
            //Adiciona individuo a nova população
            newPopulation.setIndividual(populationIndex, individual);
        }
        return newPopulation;
    }
//Nova Classe

public class Individual {
    private int[] chromosome;
    private double fitness = -1;

    public Individual(int[] chromosome) {
        this.chromosome = chromosome;
    }

    public Individual (int chromosomeLenght) {
        this.chromosome = new int [chromosomeLenght];
        for (int gene = 0; gene < chromosomeLenght; gene ++) {
            if (0.5 < Math.random()) {
                this.setGene(gene, 1);
            } else {
                this.setGene(gene, 0);
            }
        }
    }

    public int[] getChromosome() {
        return this.chromosome;
    }

    public int getChromosomeLenght() {
        return this.chromosome.length;
    }

    public void setGene(int offset, int gene) {
        this.chromosome[offset] = gene;
    }

    public int getGene(int offset) {
        return this.chromosome[offset];
    }

    public void setFitness(double fitness) {
        this.fitness = fitness;
    }

    public double getFitness() {
        return this.fitness;
    }

    public String toString() {
        String output = "";
        for (int gene = 0; gene < this.chromosome.length; gene++) {
            output += this.chromosome[gene];
        }
        return output;
    }    
//Nova Classe

public class Population {
    private Individual population[];
    private double populationFitness = -1;

    public Population(int populationSize) {
        this.population = new Individual[populationSize];
    }

    public Population (int populationSize, int chromosomeLenght) {
        this.population = new Individual[populationSize];

        for (int individualCount = 0; individualCount < populationSize; individualCount++) {
            Individual individual = new Individual(chromosomeLenght);
            this.population[individualCount] = individual;
        }
    }

    public Individual[] getIndividuals() {
        return this.population;
    }

    public Individual getFittest(int offset) {
        Arrays.sort(this.population, new Comparator<Individual>(){
            @Override
            public int compare(Individual o1, Individual o2) {
                if (o1.getFitness() > o2.getFitness()) {
                    return -1;
                } else if (o1.getFitness() < o2.getFitness()) {
                    return 1;
                }
                return 0;
            }
        });
        return this.population[offset];
    }

    public void setPopulationFitness(double fitness) {
        this.populationFitness = fitness;
    }

    public double getPopulationFitness() {
        return this.populationFitness;
    }

    public int size() {
        return this.population.length;
    }

    public Individual setIndividual(int offset, Individual individual) {
        return population[offset] = individual;
    }

    public Individual getIndividual(int offset) {
        return population[offset];
    }

    public void shuffle() {
        Random rnd = new Random();
        for (int i = population.length - 1; i > 0; i--) {
            int index = rnd.nextInt(i + 1);
            Individual a = population[index];
            population[index] = population[i];
            population[i] = a;
        }
    }

Exception:

Exception in thread "main" java.lang.NullPointerException
        at chapter2.GeneticAlgorithm.calcFitness(GeneticAlgorithm.java:‌​27)
        at chapter2.GeneticAlgorithm.evalPopulation(GeneticAlgorithm.ja‌​va:46)
        at chapter2.AllOnesGA.main(AllOnesGA.java:12) 
C:\Users\edega\AppData\Local\NetBeans\Cache\8.2\executor-sni‌​ppets\run.xml:53: Java returned: 1
FALHA NA CONSTRUÇÃO (tempo total: 0 segundos)

I wonder what I can do to fix this problem.

  • I think in all classes except the first, you forgot to put the package chapter2; and the } at the end of class.

  • Reading the error line, an object is being used that has null on the line 27, now just try to figure out why this object has null

  • I’m working on this code. Your project is really cool, it just had some silly mistakes and details to improve.

  • I already have an answer ready. I voted to reopen the question.

  • If you could pass me the corrected code, I’d be grateful!

  • 1

    This question is being debated at the goal: https://pt.meta.stackoverflow.com/q/6237/132

Show 1 more comment

1 answer

4


Your mistake is because in main, you create an object of the type Population using the constructor that only receives one int. This constructor creates the array of Individual, but does not fill it with anything, and therefore all positions will be null.

When the evalPopulation is called in this empty population, the population.getIndividuals() of the loop for will bring this array to be iterated. This way, in the loop for of the method evalPopulation, the variable individual will be null, and this will be passed on to calcFitness.

In the method calcFitness, since the individual is null, the individual.getChromosomeLength() in the stopping condition of the for will give a NullPointerException.

There are very simple and quick ways to solve this, but I decided to look for a deeper approach. Your code until it is reasonably well written, but there are a few little problems that can be arranged.

The first thing you notice is that your method calcFitness looks at the amount of 1’s found in the individual’s genes. However, genetic algorithms for different problems use different ways to evaluate fitness. Therefore, it is important that the way to calculate the fitness can be plugged in and/or defined by those using the class GeneticAlgorithm, as are the other values of this class (populationSize, crossoverRate, mutationRate and elitismCount). So we can specify an interface that says what a fitness calculation is:

package chapter2;

@FunctionalInterface
public interface FitnessFunction {
    public double getFitness(boolean[] chromosome);
}

Did you notice the boolean[] in the method parameter? Well, your class Individual works with int[]. Meanwhile the intused s are always 0 and 1, so it is convenient to use boolean[] instead of int[].

Part of the problem that caused you to make a mistake that caused a NullPointerException was to be working with an object Population incomplete and not properly initialized. Working with objects in incomplete or uninitialized states is a bad programming practice, but not enough is said about it.

A weapon against this class of problems is the principle that the constructor should not only instantiate the object, but return an already properly initialized and fully filled instance.

Another weapon against the problems of working with inconsistent objects is immutability, because it ensures that the object created will be in a valid state and cannot leave it. If an object in a different state is desired, a new object must be created. Immutability is very useful in Strings, for example, by ensuring that a reference to "xyz" cannot mysteriously and suddenly become a reference to "abc".

Putting these concepts together, let’s define the class Individual as an immutable class:

package chapter2;

import java.util.Random;

public final class Individual {

    private static final Random RND = new Random();

    private final boolean[] chromosome;
    private final double fitness;
    private final FitnessFunction fitnessCalc;

    private Individual(FitnessFunction fitnessCalc, boolean[] chromosome) {
        this.chromosome = chromosome;
        this.fitnessCalc = fitnessCalc;
        this.fitness = fitnessCalc.getFitness(chromosome);
    }

    public static Individual randomIndividual(FitnessFunction fitnessCalc, int chromosomeLenght) {
        boolean[] chromosome = new boolean[chromosomeLenght];
        for (int gene = 0; gene < chromosomeLenght; gene++) {
            chromosome[gene] = RND.nextInt(2) == 0;
        }
        return new Individual(fitnessCalc, chromosome);
    }

    public static Individual cross(Individual parent1, Individual parent2) {
        boolean[] chromosome = new boolean[parent1.getChromosomeLenght()];
        for (int geneIndex = 0; geneIndex < chromosome.length; geneIndex++) {
            //Usa metade dos genes do parent1 e metade dos genes do parent2.
            Individual parent = RND.nextInt(2) == 0 ? parent1 : parent2;
            chromosome[geneIndex] = parent.getGene(geneIndex);
        }
        return new Individual(parent1.fitnessCalc, chromosome);
    }

    public Individual newMutant(double mutationRate) {
        // Cria os cromossomos do novo indivíduo inicialmente como uma cópia do cromossomo deste indivíduo.
        boolean[] newChromosome = chromosome.clone();

        // Percorre os cromossomos do novo indivíduo e troca alguns genes aleatoriamente, respeitando a taxa de mutação.
        for (int geneIndex = 0; geneIndex < chromosome.length; geneIndex++) {
            if (mutationRate > RND.nextDouble()) {
                newChromosome[geneIndex] ^= true;
            }
        }

        // Cria o novo indivíduo com o novo cromossomo mutado.
        return new Individual(fitnessCalc, newChromosome);
    }

    private int getChromosomeLenght() {
        return this.chromosome.length;
    }

    private boolean getGene(int offset) {
        return this.chromosome[offset];
    }

    public double getFitness() {
        return this.fitness;
    }

    @Override
    public String toString() {
        char[] c = new char[chromosome.length];
        for (int geneIndex = 0; geneIndex < chromosome.length; geneIndex++) {
            c[geneIndex] = chromosome[geneIndex] ? '1' : '0';
        }
        return new String(c);
    }
}

This class above deserves some explanations, since some things of its original code that were not there were inside and besides, the structure is significantly different from its original class. The main reason for this is the envious methods.

A jealous method is a method that "wanted" to be within one class, but is in another. That is, it is a method that has been placed in the wrong class. Ideally, a method represents an operation performed on the class containing it. But a jealous method that "envies" another class, rather than representing an operation on the class that contains it, represents an operation on another class.

In its original code, virtually everything in the class GeneticAlgorithm are methods that wanted to be in class Population. Already the method calcFitness wanted to be in class Individual. The methods crossoverPopulation and mutatePopualtion are envious of class Population, but within the bonds for of both, it is possible to extract a passage in each one that is envious of Individual. After the extraction of these sections and their change to the class Individual, we have the methods cross and newMutant in class Individual.

Another thing to note is the immutability of Individual. Note that the constructor is private, all fields are final and the class is final. This is to ensure that when the object is instantiated, it has already been fully initialized and cannot be changed. Note that the fitness calculation is done within the builder itself, which eliminates the need for you to have to call a method setFitness or calcFitness on the subject. The need for a setFitness or calcFitness has to be called after completion of the constructor’s execution demonstrates that the object had not been built entirely and that this method served to finish building it. Therefore, the best place to calculate fitness is in the builder (calculation this delegate to FitnessFunction).

A mutation (method mutate) in a Individual is the creation of another Individual with a slightly different chromosome. A cross between two Individuals (method cross) also creates a new Individual without altering either parent. From its original builder it received a int, did the method randomIndividual. The fact that the constructor is private ensures that it is impossible to create instances of Individual in any way other than by means of one of these three methods, which ensures that the rules for the creation of the Individual are respected.

Moreover, as a side effect of eliminating envy of methods, the class Individual has less need to expose getters or other methods to be used by other classes, and as an effect encapsulation is improved, class coupling is reduced and the class cohesion is augmented. This made it possible to remove the method getChromosome() and reducing the visibility of getGene of public for private.

Use the class StringBuilder to create Stringcomplex s. Stay concatenating Strings within a loop is a bad programming practice as it creates a lot of intermediate objects String Mismatches that consume memory and have to have their contents copied every time something is concatenated, impairing performance. At this point, the fact of StringBuilder nay unchanging is an advantage.

A similar case where mutability is used is in the methods randomIndividual, mutate and cross, that take advantage of the mutability of arrays to ensure a good performance and only instantiate the Individual once at the end. Otherwise, or we would have to sacrifice the immutability of Individual or we would have to instantiate several Individuals intermediate each with a different gene from the previous one, and having to copy the chromosomes several times.

Finally, the last detail to notice in the class Individual, is that we only need one instance of Random. Therefore, it is maintained in a variable private static final.

Taking away so much of the class GeneticAlgorithm because of envious methods would lead to class Population need several of the values that are in GeneticAlgorithm. To avoid creating a strong coupling between these classes, I created the class GeneticAlgorithmParameters:

package chapter2;

public final class GeneticAlgorithmParameters {
    private final double crossoverRate;
    private final double mutationRate;
    private final int elitismCount;
    private final int populationSize;
    private final int chromosomeLength;
    private final FitnessFunction fitnessCalc;

    public GeneticAlgorithmParameters(
            double crossoverRate,
            double mutationRate,
            int elitismCount,
            int populationSize,
            int chromosomeLength,
            FitnessFunction fitnessCalc)
    {
        this.crossoverRate = crossoverRate;
        this.mutationRate = mutationRate;
        this.elitismCount = elitismCount;
        this.populationSize = populationSize;
        this.chromosomeLength = chromosomeLength;
        this.fitnessCalc = fitnessCalc;
    }

    public double getCrossoverRate() {
        return crossoverRate;
    }

    public double getMutationRate() {
        return mutationRate;
    }

    public int getElitismCount() {
        return elitismCount;
    }

    public int getPopulationSize() {
        return populationSize;
    }

    public int getChromosomeLength() {
        return chromosomeLength;
    }

    public FitnessFunction getFitnessCalc() {
        return fitnessCalc;
    }
}

This class does not have much special about it, but it is to be noted that it is immutable. It represents the parameters with which your genetic algorithm operates: what is the population, what is the size of chromosomes, what is the fitness function, mutation rate, etc.

Now, let’s go to class Population:

package chapter2;

import java.util.ArrayList;
import java.util.List;

public final class Population {

    private final int generationNumber;
    private final GeneticAlgorithmParameters parameters;
    private final List<Individual> individuals;
    private final double populationFitness;

    private Population(
            int generationNumber,
            GeneticAlgorithmParameters parameters,
            List<Individual> individuals)
    {
        // Seta vários dos atributos desta população.
        this.generationNumber = generationNumber;
        this.parameters = parameters;
        this.individuals = individuals;

        // Ordena os indivíduos por fitness.
        individuals.sort((o1, o2) -> (int) Math.signum(o2.getFitness() - o1.getFitness()));

        // Calcula o fitness da população.
        this.populationFitness = individuals.stream().mapToDouble(Individual::getFitness).sum();
    }

    public static Population randomPopulation(GeneticAlgorithmParameters parameters) {
        // Cria uma lista inicialmente vazia dos indivíduos que serão gerados.
        List<Individual> individuals = new ArrayList<>(parameters.getPopulationSize());

        // Coloca na lista vários indivíduos gerados aleatoriamente.
        for (int individualCount = 0; individualCount < parameters.getPopulationSize(); individualCount++) {
            Individual individual = Individual.randomIndividual(
                    parameters.getFitnessCalc(),
                    parameters.getChromosomeLength());
            individuals.add(individual);
        }

        // Cria uma população na geração 1 com os indivíduos gerados.
        return new Population(1, parameters, individuals);
    }

    public Individual getFittest() {
        // Retorna o primeiro indivíduo (o que tem o melhor fitness).
        return individuals.get(0);
    }

    public int getGenerationNumber() {
        return generationNumber;
    }

    public boolean isTerminationConditionMet() {
        return individuals.stream().anyMatch(individual -> individual.getFitness() == 1);
    }

    private Individual selectParent() {
        // Crossover aplicado com o método "Roleta de Cassino".
        // Gira a roleta.
        double rouletteWheelPosition = Math.random() * populationFitness;

        // Encontra o indivíduo para o crossover.
        double spinWheel = 0;
        for (Individual individual : individuals) {
            spinWheel += individual.getFitness();
            if (spinWheel >= rouletteWheelPosition) {
                return individual;
            }
        }

        // Se a roleta não parou em nenhum indivíduo, escolhe o último.
        return individuals.get(individuals.size() - 1);
    }

    public Population crossover() {
        // Cria uma lista inicialmente vazia dos indivíduos que serão gerados.
        List<Individual> newIndividuals = new ArrayList<>(parameters.getPopulationSize());

        // Loop sobre população atual por fitness.
        int populationIndex = 0;
        for (Individual parent1 : individuals) {

            // Aplicar crossover a este indivíduo?
            if (parameters.getCrossoverRate() > Math.random() && populationIndex > parameters.getElitismCount()) {

                // Encontra o par para realizar o crossover.
                Individual parent2 = selectParent();

                // Realiza o crossover e cria um novo indivíduo.
                Individual offspring = Individual.cross(parent1, parent2);

                // Adiciona o novo indivíduo gerado à lista da nova população.
                newIndividuals.add(offspring);
            } else {
                // Adiciona o indivíduo à lista da nova população sem aplicar crossover.
                newIndividuals.add(parent1);
            }
        }

        // Cria uma nova população da geração seguinte com os indivíduos gerados.
        return new Population(generationNumber + 1, parameters, newIndividuals);
    }

    public Population mutate() {
        // Cria uma lista inicialmente vazia dos indivíduos que serão gerados.
        List<Individual> newIndividuals = new ArrayList<>(parameters.getPopulationSize());

        // Loop sobre população atual por fitness.
        int populationIndex = 0;
        for (Individual individual : individuals) {

            // Faz mutação se o indivíduo não for elite.
            Individual newIndividual = populationIndex >= parameters.getElitismCount()
                    ? individual.newMutant(parameters.getMutationRate())
                    : individual;

            // Adiciona o indivíduo a nova população.
            newIndividuals.add(newIndividual);

            // Conta um indivíduo a fim de podermos contar quantos
            // estão na elite e quantos sofrerão mutação.
            populationIndex++;
        }

        // Cria uma nova população da geração seguinte com os indivíduos gerados.
        return new Population(generationNumber + 1, parameters, newIndividuals);
    }

    @Override
    public String toString() {
        return "Generation " + generationNumber
                + ", best fitness " + getFittest().getFitness()
                + ", individuals " + individuals.toString();
    }
}

The first thing to notice here is that I use List<Individual> instead of Individual[]. That in itself already diminishes the possibility of you having one NullPointerException because the list will never have null elements even when newly instantiated, unlike what happens with the array. When using lists instead of array, you also have to worry less about tracking the size. With lists, there is also much less need to keep counting and calculating positions of elements.

The class Population is immutable and should be instantiated with the definitive list of individuals. The method setIndividual of its original class was eliminated as it was used only to fill the Population newly created. That is, the method setIndividual served to finish building the object that the constructor left incomplete, exactly what we seek to eliminate. The constructor is also private and instances can only be created by methods mutate, cross and randomPopulation, and the first two are concerned with increasing the number of generation (the last one creates generation 1). The lists used by these three methods are also mutable while being filled in, being treated as immutable when this list is passed to the constructor of Population.

In addition, the builder of Population already orders individuals according to fitness, so that this operation is no longer a construction step of the object that the builder did not do. With the calculation of populationFitness, we also have the same situation again (which made the method disappear evalPopulation). And on top of that, note that the code for these two activities (sort and measure the fitness of the population) is much simpler using Java 8’s lambda syntax. lambda is also used to simplify the isTerminationConditionMet.

As for the generation number, I had the impression that this was something I was jealous of the class Population, and so I put it in there. Another effect of improving encapsulation was that the method getFittest now no longer need the parameter int, only the highest fitness individual needs to be accessed externally.

Part of your class AllOnesGA consisted of the genetic algorithm itself, which is the algorithm that produces as many generations of populations as needed, evaluates them, crossover them and mutations. This was then moved to the class GeneticAlgorithm, that has remained so:

package chapter2;

public class GeneticAlgorithm {
    private final GeneticAlgorithmParameters params;

    public GeneticAlgorithm(GeneticAlgorithmParameters params) {
        this.params = params;
    }

    public Population run(int maxGenerations) {

        // Inicializa a população.
        Population population = Population.randomPopulation(params);
        System.out.println("Start population: " + population);

        // Produz tantas gerações quanto forem necessárias até que o número
        // máximo de gerações tenha sido atingido ou uma solução ideal tenha
        // sido encontrada.
        while (population.getGenerationNumber() < maxGenerations
                && !population.isTerminationConditionMet())
        {
            // Aplica o crossover (conta uma nova geração).
            population = population.crossover();
            System.out.println("New population: " + population);

            // Aplica mutação (conta uma segunda nova geração).
            population = population.mutate();
            System.out.println("New population: " + population);
        }

        return population;
    }
}

Note that in this class there is nothing of its original class. What was in the original class were envious methods that are now in the right place, and what was left here was behavior that was envious for not being here.

The only thing is that he ends up counting a generation to the crossover and another for the mutate, time with each iteration of the while, each of these creates a new Population which implies two generations for iteration of the while.

Finally, note that there is a parameter maxGenerations to avoid the method getting stuck in an infinite loop or a solution taking days, months, or years to come out. Upon reaching the maximum number of iterations, the last generated population is returned (which should be headed by the individual with the highest fitness ever found).

And finally your class AllOnesGA:

package chapter2;

public class AllOnesGA {

    // Função de avaliação de fitness de teste.
    // O valor máximo dela é quando todos os valores do cromossomo são verdadeiros.
    // Esta função é apenas para exemplificar, pois numa aplicação real, o valor
    // ótimo do cromossomo é difícil de ser determinado e a implementação desta
    // função seria bem complicada e difícil de otimizar manualmente ou por uso
    // de técnicas mais ortodoxas.
    private static double simpleFitness(boolean[] chromosome) {
        // Rastreia o número de genes corretos.
        int correctGenes = 0;

        // Loop sobre cada gene.
        for (boolean b : chromosome) {
            // Adiciona um ponto de fitness para cada true encontrado.
            if (b) correctGenes++;
        }

        // Cálculo de fitness.
        return (double) correctGenes / chromosome.length;
    }

    public static void main(String[] args) {
        double crossoverRate = 0.05;
        double mutationRate = 0.01;
        int elitismCount = 3;
        int populationSize = 80;
        int chromosomeCount = 15;
        int maxGenerations = 10000;

        // Cria os parâmetros do algoritmo genético (inclusive a função de fitness).
        GeneticAlgorithmParameters params = new GeneticAlgorithmParameters(
                crossoverRate,
                mutationRate,
                elitismCount,
                populationSize,
                chromosomeCount,
                AllOnesGA::simpleFitness);

        // Cria instância do algoritmo genético com os parâmetros dados (inclusive a função de fitness).
        GeneticAlgorithm ga = new GeneticAlgorithm(params);

        // Executa o algoritmo genético até obter a população final.
        Population finalPopulation = ga.run(maxGenerations);

        // Mostra a população final.
        System.out.println("Final population: " + finalPopulation);
        Individual fittest = finalPopulation.getFittest();
        System.out.println("Best solution: " + fittest + " - Fitness: " + fittest.getFitness());
    }
}

The method simpleFitness is your original fitness method, which checks to see if all genes are true. Other fitness calculation methods can be used depending on the specific problem in which you will seek a solution with genetic algorithms.

The method main He doesn’t have a lot of secrets either. It creates the parameters of the genetic algorithm (can change them to play at will), instantiates the genetic algorithm, executes it and shows the result.

To conclude, remember that:

  • The ternary operator is your friend if you know how to use it, and it can eliminate those ifs boring where what is in the block of if is almost the same as what is in the block of else, avoiding duplication of code.

  • If you’re going to use an expression in the form blablabla == false, being blablabla an expression of the kind bololean, use only !blablabla instead. The same goes for blablabla == true, just use blablabla.

  • Use blablabla ^= true; is a trick to reverse a value of type boolean. Very useful when writing blablabla = !blablabla is bad when the expression blablabla becomes a very complex expression such as newChromosome[geneIndex] class Individual. For example, newChromosome[geneIndex] ^= true; is simpler than newChromosome[geneIndex] = !newChromosome[geneIndex].

Browser other questions tagged

You are not signed in. Login or sign up in order to post.