package examples; import org.jgap.*; import org.jgap.impl.*; public class KMTester extends FitnessFunction { private final int m_targetAmount; /** * Constructs this MinimizingMakeChangeFitnessFunction with the desired * amount of change to make. * * @param a_targetAmount The desired amount of change, in cents. This * value must be between 1 and 99 cents. */ public KMTester( int a_targetAmount ) { if( a_targetAmount < 1 || a_targetAmount > 99 ) { throw new IllegalArgumentException( "Change amount must be between 1 and 99 cents." ); } m_targetAmount = a_targetAmount; } /** * Determine the fitness of the given Chromosome instance. The higher the * return value, the more fit the instance. This method should always * return the same fitness value for two equivalent Chromosome instances. * * @param a_subject: The Chromosome instance to evaluate. * * @return A positive integer reflecting the fitness rating of the given * Chromosome. */ public double evaluate( IChromosome a_subject ) { // The fitness value measures both how close the value is to the // target amount supplied by the user and the total number of coins // represented by the solution. We do this in two steps: first, // we consider only the represented amount of change vs. the target // amount of change and calculate higher fitness values for amounts // closer to the target, and lower fitness values for amounts further // away from the target. If the amount equals the target, then we go // to step 2, which adjusts the fitness to a higher value for // solutions representing fewer total coins, and lower fitness // values for solutions representing a larger total number of coins. // ------------------------------------------------------------------ int changeAmount = amountOfChange( a_subject ); int totalCoins = getTotalNumberOfCoins( a_subject ); int changeDifference = Math.abs( m_targetAmount - changeAmount ); // Step 1: Determine the distance of the amount represented by the // solution from the target amount. Since we know the maximum amount // of change is 99 cents, we'll subtract from that the difference // between the solution amount and the target amount. That will give // the desired effect of returning higher values for amounts close // to the target amount and lower values for amounts further away // from the target amount. // ------------------------------------------------------------------ double fitness = ( 99 - changeDifference ); // Step 2: If the solution amount equals the target amount, then // we add additional fitness points for solutions representing fewer // total coins. // ----------------------------------------------------------------- if( changeAmount == m_targetAmount ) { fitness += 100 - ( 10 * totalCoins ); } return fitness; } /** * Calculates the total amount of change (in cents) represented by * the given chromosome and returns that amount. * * @param a_potentialSolution The potential solution to evaluate. * @return The total amount of change (in cents) represented by the * given solution. */ public static int amountOfChange( IChromosome a_potentialSolution ) { int numQuarters = getNumberOfCoinsAtGene( a_potentialSolution, 0 ); int numDimes = getNumberOfCoinsAtGene( a_potentialSolution, 1 ); int numNickels = getNumberOfCoinsAtGene( a_potentialSolution, 2 ); int numPennies = getNumberOfCoinsAtGene( a_potentialSolution, 3 ); return ( numQuarters * 25 ) + ( numDimes * 10 ) + ( numNickels * 5 ) + numPennies; } /** * Retrieves the number of coins represented by the given potential * solution at the given gene position. * * @param a_potentialSolution The potential solution to evaluate. * @param a_position The gene position to evaluate. * @return the number of coins represented by the potential solution * at the given gene position. */ public static int getNumberOfCoinsAtGene( IChromosome a_potentialSolution, int a_position ) { Integer numCoins = (Integer) a_potentialSolution.getGene(a_position).getAllele(); return numCoins.intValue(); } /** * Returns the total number of coins represented by all of the genes in * the given chromosome. * * @param a_potentialsolution The potential solution to evaluate. * @return The total number of coins represented by the given Chromosome. */ public static int getTotalNumberOfCoins( IChromosome a_potentialsolution ) { int totalCoins = 0; int numberOfGenes = a_potentialsolution.size(); for( int i = 0; i < numberOfGenes; i++ ) { totalCoins += getNumberOfCoinsAtGene( a_potentialsolution, i ); } return totalCoins; } public static void main(String[] args) throws Exception { // Start with a DefaultConfiguration, which comes setup with the // most common settings. // ------------------------------------------------------------- Configuration conf = new DefaultConfiguration(); // Set the fitness function we want to use, which is our // MinimizingMakeChangeFitnessFunction that we created earlier. // We construct it with the target amount of change provided // by the user. // ------------------------------------------------------------ int targetAmount = 87;//Integer.parseInt(args[0]); FitnessFunction myFunc = new KMTester( targetAmount ); conf.setFitnessFunction( myFunc ); // Now we need to tell the Configuration object how we want our // Chromosomes to be setup. We do that by actually creating a // sample Chromosome and then setting it on the Configuration // object. As mentioned earlier, we want our Chromosomes to // each have four genes, one for each of the coin types. We // want the values of those genes to be integers, which represent // how many coins of that type we have. We therefore use the // IntegerGene class to represent each of the genes. That class // also lets us specify a lower and upper bound, which we set // to sensible values for each coin type. // -------------------------------------------------------------- Gene[] sampleGenes = new Gene[ 4 ]; sampleGenes[0] = new IntegerGene(conf, 0, 3 ); // Quarters sampleGenes[1] = new IntegerGene(conf, 0, 2 ); // Dimes sampleGenes[2] = new IntegerGene(conf, 0, 1 ); // Nickels sampleGenes[3] = new IntegerGene(conf, 0, 4 ); // Pennies Chromosome sampleChromosome = new Chromosome(conf, sampleGenes ); conf.setSampleChromosome( sampleChromosome ); // Finally, we need to tell the Configuration object how many // Chromosomes we want in our population. The more Chromosomes, // the larger the number of potential solutions (which is good // for finding the answer), but the longer it will take to evolve // the population each round. We'll set the population size to // 500 here. // -------------------------------------------------------------- conf.setPopulationSize( 500 ); Genotype population = Genotype.randomInitialGenotype( conf ); population.evolve(); IChromosome bestSolutionSoFar = population.getFittestChromosome(); System.out.println( "The best solution contained the following: " ); System.out.println( KMTester.getNumberOfCoinsAtGene( bestSolutionSoFar, 0 ) + " quarters." ); System.out.println( KMTester.getNumberOfCoinsAtGene( bestSolutionSoFar, 1 ) + " dimes." ); System.out.println( KMTester.getNumberOfCoinsAtGene( bestSolutionSoFar, 2 ) + " nickels." ); System.out.println( KMTester.getNumberOfCoinsAtGene( bestSolutionSoFar, 3 ) + " pennies." ); System.out.println( "For a total of " + KMTester.amountOfChange( bestSolutionSoFar ) + " cents in " + KMTester.getTotalNumberOfCoins( bestSolutionSoFar ) + " coins." ); } }