001: /*
002: * This file is part of JGAP.
003: *
004: * JGAP offers a dual license model containing the LGPL as well as the MPL.
005: *
006: * For licensing information please see the file license.txt included with JGAP
007: * or have a look at the top of class org.jgap.Chromosome which representatively
008: * includes the JGAP license policy applicable for any file delivered with JGAP.
009: */
010: package examples.audit;
011:
012: import org.jgap.*;
013:
014: /**
015: * Sample fitness function for the coins example.
016: *
017: * @author Klaus Meffert
018: * @since 2.2
019: */
020: public class CoinsExampleFitnessFunction extends FitnessFunction {
021: /** String containing the CVS revision. Read out via reflection!*/
022: private final static String CVS_REVISION = "$Revision: 1.4 $";
023:
024: private final int m_targetAmount;
025:
026: public static final int MAX_BOUND = 1000;
027:
028: public CoinsExampleFitnessFunction(int a_targetAmount) {
029: if (a_targetAmount < 1 || a_targetAmount >= MAX_BOUND) {
030: throw new IllegalArgumentException(
031: "Change amount must be between 1 and " + MAX_BOUND
032: + " cents.");
033: }
034: m_targetAmount = a_targetAmount;
035: }
036:
037: /**
038: * Determine the fitness of the given Chromosome instance. The higher the
039: * return value, the more fit the instance. This method should always
040: * return the same fitness value for two equivalent Chromosome instances.
041: *
042: * @param a_subject the Chromosome instance to evaluate
043: *
044: * @return a positive integer reflecting the fitness rating of the given
045: * Chromosome
046: *
047: * @author Klaus Meffert
048: * @since 2.2
049: */
050: public double evaluate(IChromosome a_subject) {
051: // The fitness value measures both how close the value is to the
052: // target amount supplied by the user and the total number of coins
053: // represented by the solution. We do this in two steps: first,
054: // we consider only the represented amount of change vs. the target
055: // amount of change and return higher fitness values for amounts
056: // closer to the target, and lower fitness values for amounts further
057: // away from the target. If the amount equals the target, then we go
058: // to step 2, which returns a higher fitness value for solutions
059: // representing fewer total coins, and lower fitness values for
060: // solutions representing more total coins.
061: // ------------------------------------------------------------------
062: int changeAmount = amountOfChange(a_subject);
063: int totalCoins = getTotalNumberOfCoins(a_subject);
064: int changeDifference = Math.abs(m_targetAmount - changeAmount);
065: // Step 1: Determine distance of amount represented by solution from
066: // the target amount. Since we know the maximum amount of change is
067: // 999 cents, we'll subtract the difference in change between the
068: // solution amount and the target amount. That will give the desired effect
069: // of returning higher values for amounts closer to the target amount and
070: // lower values for amounts further away from the target amount.
071: // -----------------------------------------------------------------
072: double fitness = (MAX_BOUND - 1 - changeDifference * 20);
073: // Step 2: If the solution amount equals the target amount, then
074: // we add additional fitness points for solutions representing fewer
075: // total coins.
076: // -----------------------------------------------------------------
077: if (changeDifference == 0) {
078: fitness += computeCoinNumberBonus(totalCoins);
079: }
080: // Make sure fitness value is always positive.
081: // -------------------------------------------
082: return Math.max(1.0d, fitness);
083: }
084:
085: protected double computeCoinNumberBonus(int a_coins) {
086: return MAX_BOUND - ((MAX_BOUND * 0.05d) * a_coins);
087: }
088:
089: /**
090: * Calculates the total amount of change (in cents) represented by
091: * the given potential solution and returns that amount.
092: *
093: * @param a_potentialSolution the pontential solution to evaluate
094: * @return the total amount of change (in cents) represented by the
095: * given solution
096: *
097: * @author Klaus Meffert
098: * @since 2.2
099: */
100: public static int amountOfChange(IChromosome a_potentialSolution) {
101: int numQuarters = getNumberOfCoinsAtGene(a_potentialSolution, 0);
102: int numDimes = getNumberOfCoinsAtGene(a_potentialSolution, 1);
103: int numNickels = getNumberOfCoinsAtGene(a_potentialSolution, 2);
104: int numPennies = getNumberOfCoinsAtGene(a_potentialSolution, 3);
105: return (numQuarters * 25) + (numDimes * 10) + (numNickels * 5)
106: + numPennies;
107: }
108:
109: /**
110: * Retrieves the number of coins represented by the given potential
111: * solution at the given gene position.
112: *
113: * @param a_potentialSolution the potential solution to evaluate
114: * @param a_position the gene position to evaluate
115: * @return the number of coins represented by the potential solution
116: * at the given gene position.
117: *
118: * @author Klaus Meffert
119: * @since 2.2
120: */
121: public static int getNumberOfCoinsAtGene(
122: IChromosome a_potentialSolution, int a_position) {
123: Integer numCoins = (Integer) a_potentialSolution.getGene(
124: a_position).getAllele();
125: return numCoins.intValue();
126: }
127:
128: /**
129: * Returns the total number of coins represented by all of the genes in
130: * the given potential solution.
131: *
132: * @param a_potentialsolution The potential solution to evaluate.
133: * @return The total number of coins represented by the given Chromosome.
134: *
135: * @since 2.2
136: */
137: public static int getTotalNumberOfCoins(
138: IChromosome a_potentialsolution) {
139: int totalCoins = 0;
140: int numberOfGenes = a_potentialsolution.size();
141: for (int i = 0; i < numberOfGenes; i++) {
142: totalCoins += getNumberOfCoinsAtGene(a_potentialsolution, i);
143: }
144: return totalCoins;
145: }
146: }
|