Project 4 - Genetic Algorithms

Undergrad portion

The program should accept the following as input:

1. Initialize the population. Generate N-bit strings each of size l. It is easier to represent the individuals as character arrays rather than integers.

For each generation, do the following:

2. Calculate the fitness of each individual. This can be done in several steps:

a. Find the integer that the individual's bit string represents. Iterate through each bit, and if the bit is a 1, then add the corresponding power of 2 to a running sum. For example, if l=20, and if the leftmost bit of the string is 1, then add 219 to the sum. Use the loop index to help you figure out which power of 2 you are on for each iteration. After looping through all the bits, this running sum will be the integer value of the bit string.

b. Plug the integer value from step a into the fitness function F(s)=(x/2l)10 to get that individual's fitness value.

c. When you are looping through each individual finding the fitness value, keep a running sum of the total fitness, which is the sum of fitness values for all N individuals.

d. For each individual, normalize its fitness value by dividing its fitness value by the total fitness from step c. It's best to store these normalized values in a separate array than the actual fitness values since you will need to keep the original fitness values for finding the statistics in a later step.

e. For each individual, compute a number which is the sum of that individual's normalized fitness value, and the normalized fitness values for each of the individuals before it. Thus, a running total is kept of the normalized fitness values. This will be helpful later when probabilistically selecting parents.

The following example should make steps d and e more clear. Note that the total fitness value is 1.95. Also note that the sum of all the normalized fitness values is 1.
Individual  Fitness value  Normalized fitness value  Running total
    0           0.05              0.0256                0.0256
    1           0.2               0.1026                0.1282
    2           0.6               0.3077                0.4359
    3           0.3               0.1538                0.5897
    4           0.8               0.4103                1.0000
Now you are set up to select parents and produce the next generation.

Perform N/2 iterations doing the following:

3. Select two individuals to be parents. First get two random numbers between 0 and 1. Then see which range the random numbers fall into based on the running total numbers computed in step 2e above. The random number will be between two of those numbers. The individual associated with the second of those numbers will be the individual selected to be a parent. For example, suppose your two random numbers are 0.4147 and 0.7395. In the example above, the number 0.4147 is between 0.1282 and 0.4359, so individual 2 is one of the parents. In the above example, the number 0.7395 is between 0.5897 and 1.0000, so individual 4 is the other parent. Be sure that you get two distinct parents when you do this step, so that you do not result in an individual mating with itself. Keep selecting a second parent until you get one that is different from the first parent.

4. Mate parents and perform any crossover to get offspring.

a. First generate a random number to determine whether crossover will be done.

b. If no crossover is done, then simply copy the bit strings of the parents into new bit strings which will represent the offspring.

c. If crossover is done, then first randomly select a bit to be the crossover point. Then copy the bit strings of the parents into the bit strings of the offspring up to the crossover point. After the crossover point, reverse which offspring gets the bits from which parent.

5. Perform any mutations on the offspring. For each of the two offspring, go through all the bits in their strings. For each bit, generate a random number to indicate whether that bit will be mutated. If the bit will be mutated, then simply flip that bit.

6. Update the population. Copy all of the offspring bit string arrays into the bit string arrays of the current population. The new offspring will replace the current population. In other words, the current population arrays will be overwritten by the offspring arrays.

7. Find the statistics of the population. Find the average fitness of the population, the fitness of the best individual, and the number of correct bits in the best individual. Find these three measures for each generation. You will use this data to make the required graphs. You might want to store all this data in arrays which you can then dump into a file later on.

Repeat all of the above for several different runs. Do not average over them as this will either result in loss of data for individual runs or the average will just be a straight line. Plot all of these runs on the same graph and/or choose one of the runs as a "typical" run and plot it.

Also repeat all of the above for several different combinations of the five parameters given at the beginning of this document. For each combination of parameters, do several runs as explained in the previous paragraph.