%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Approximate Genetic Algorithm %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Kevin Passino % Version: 3/12/99 % % This program simulates the optimzation of a simple function with % an approximation to the genetic algorithm (which we call AGA). % First, we outline some background % information on the AGA and define some terminology. Following this, % we explain how to use the program for your own purposes and as an % example we show how to solve an optimization problem. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Background Information on GAs and the AGA: % % A genetic algorithm is an optimization method that % manipulates a string of numbers in a manner similar to how chromosomes % are changed in biological evolution. An initial population made up of % strings of numbers is chosen at random or is specified by the % user. Each string of numbers is called a "chromosome" or an % "individual," and each number slot is called a "gene." A % set of chromosomes forms a population. Each chromosome % represents a given number of traits which are the actual % parameters that are being varied to optimize the "fitness function". % The fitness function is a performance index that we seek to maximize. % % The operation of the GA proceeds in steps. Beginning with the initial % population, "selection" is used to choose which chromosomes % should survive to form a "mating pool." Chromosomes are chosen based on % how fit they are (as computed by the fitness function) relative % to the other members of the population. More fit individuals end up % with more copies of themselves in the mating pool so that they % will more significantly effect the formation of the next % generation. Next, several operations are taken on the mating % pool. First, "crossover" (which represents mating, the exchange % of genetic material) occurs between parents. % To perform crossover, a random spot is picked % in the chromosome, and the genes after this spot are switched % with the corresponding genes of the other parent. Following this, % "mutation" occurs. This is where some genes are randomly changed % to other values. After the crossover and mutation operations % occur, the resulting strings form the next generation % and the process is repeated. A termination criterion % is used to specify when the GA should end (e.g., the maximum % number of generations or until the fitness stops increasing). % % Normally, the GA is implemented using a base-2 or base-10 % representation. If you use a base-2 representation generally you will % have to convert the problem from its base-10 representation in the % real-world to a base-2 representation and to get data about program % operation you will have to convert the base-2 strings that the GA % operates on back to base-10. So, what is an "approximate" GA? In a computer % it takes time to convert numbers between different bases and even if % a base-10 representation is used in a computer you normally have to % change the numbers into sequences of digits that can be independently % manipulated by genetic operators (e.g., for crossover, to swap digits % on a string). AGA is a version of a genetic algorithm that seeks % to avoid encoding and decoding in traditional GAs by working with the % real number system that is used in the computer. Significant % computational savings can be obtained and AGA has been used to % study the use of GAs in on-line real-time optimization. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Some GA Definitions for this Program: % % A GENE is a digit position that can take on certain values % Ex: In a base-10 algorithm a gene can hold any number between 0 and 9. % % A CHROMOSOME is a string of genes. % Ex: In base-10 1234567890 could be a chromosome of length 10. % % A TRAIT is a decimal number which is decoded from a chromosome. % Normally, a chromosome is a concatenation of several TRAITS. % % An INDIVIDUAL is the object that the GA is attempting to optimize. % An individual is described by its chromosome. % The individual's traits determine its fitness. % % A POPULATION is a set of individuals (set of chromosomes). % % FITNESS FUNCTION is the objective function to be optimized which provides % the mechanism for evaluating each string (we maximize the fitness % function). % % SELECTION: a fitter string receives a higher number of offspring % and thus has a higher chance of surviving in the subsequent generation. % % CROSSOVER is an operation which swaps genes between two chromosome. % % MUTATION is flipping a digit in the chromosome after crossover. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % How to use the program for your own purposes: % % The main thing that needs to be changed is the fitness function % and a few parameters. It is now set to minimize a function z=f(x,y) % that is a sum of scaled translated Gaussian distributions with x and y % between 0 and 30 (specified by the elements of HIGHTRAIT and LOWTRAIT). % The function to be optimized must be changed in the section specified % in the program below. % % In addition, the following parameters should be modified according % to your needs: % % 1. NUM_TRAITS - the number of traits represented by each chromosome % (e.g. if we were trying to optimize the above function % we could have NUM_TRAITS=2). % Example: Chromosome: 123456789012 % Possible two traits: 123456, 789012 % % 2. HIGHTRAIT,LOWTRAIT - arrays with NUM_TRAITS elements with each element % specifying the upper (lower) limit on each trait (often % known a priori for an optimization problem). % For example, we may only want to find the optimum values % of a function over a certain region. For instance, % we could choose HIGHTRAIT=[30 30] and LOWTRAIT=[0 0] % for the above optimization problem. % % 3. MUTAT_PROB - the probability that a mutation will occur in any given % gene (i.e., that a gene will be switched to another % element of the alphabet that is being used). Note that % for base 10 we randomly choose a number between 0-9, % with the exception of the number that was being mutated. % % 4. CROSS_PROB - the probability that a crossover will occur between two % parents (standard swapping of genes is used). % % 5. SELF_ENTERED - if this flag is set to 1, an initial % population can be entered by the user. If it is 0, % a random initial population is created. We enter the % initial population in a special convenient way for % the user. % % 6. POP_SIZE - the number of individuals in a population (fixed). % This should be an even number - since pairs mate. % % 7. ELITISM - if this flag is set to one, the most fit individual in each % generation will be carried over to the next generation % without being modified in any way by the genetic operators. % % 8. DELTA, EPSILON - these are for the termination criterion. % The program is terminated if the fitness % changes less than EPSILON over DELTA % generations. % % 9. MAX_GENERATION - the number of generations that the program will % produce before stopping. This indicates the length % of the program will run. If it takes too long % to run, this number can be lowered. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Next, we provide some ideas on how to run to the program to illustrate % its operation. Proceed as follows: % % (1) Experiment with the intial population. Enter your own initial % population (to do this you modify the code and let SELF_ENTERED = 1). % Currently, the code sets the intial population to be all zeros if % you let SELF_ENTERED = 1. Another choice you may want to consider % is to place the initial members of the population on some type of % uniform grid to try to make sure that at least one of the intial % points is in the proximity of the final point. Currently, if you let % SELF_ENTERED = 0, the program automatically generates a random initial % population with a uniform distribution on the allowable range of each % trait. % (2) Next, you can experiment with the settings for the crossover and % mutation probabilities. First, try MUTAT_PROB=0.05; CROSS_PROB=0.8. % If you lower the crossover probability you will generally find that % there will be less local search near the best individuals (i.e., % it will tend to less often interpolate between good individuals to % try to find a solution). If you raise the mutation probability it % will tend to cause more random excursions into possibly unexplored % regions. Too high of a mutation probability can cause degradation % to random search, where too low of a mutation probability can cause % the GA to lock in on a local minima and never find the global one (at % least in a reasonable amount of time). % (3) Next, you can experiment with the population size. Larger population % sizes require more computation time but they can speed convergence. % For instance, for a larger population size you will have more guesses % of where the global maximum is for the intial population and hence % it is more likely that there will be at least one good intial guess. % There are similar effects as the algorithm runs. % (4) Next, turn on elitism. You will find that elitism removes some of the % effects of having a higher mutation rate since it makes sure that % the best candidate is not disturbed. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% clear % Initialize momery rand('state',0) % Reset the random number generator so each time you re-run % the program with no changes you will get the same results. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % User: Input your own parameters below (the ones given are for the % optimization problem given above): %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% NUM_TRAITS=2; % Number of traits in each individual HIGHTRAIT=[30 30]; % Upper limit of a trait LOWTRAIT=[0 0]; % Lower limit of a trait MUTAT_PROB=0.05; % Probability of mutation (typically <.1) CROSS_PROB=0.8; % Probability of crossover (typically near 1) SELF_ENTERED=0; % "0": a random initial population. % "1": a specified initial population % If you choose "1", enter it in the program. POP_SIZE=20; % Number of individuals in the population ELITISM=0; % Elitism ON/OFF, 1/0 DELTA=100; % Number of generations to be counted % for the termination criteria. EPSILON = 0.01; % Range that the fitness must change % in the termination criteria. MAX_GENERATION=1000; % Number of times the loop will run before % giving up on the EPSILON-DELTA termination cond. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Next, we make the initial population of size = POP_SIZE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% popcount=1; % Initialize the generation count, % set it to one, the first population if SELF_ENTERED == 0 % Make a random initial population for % base-10 operation by specifying the % matrix of initial traits for pop_member = 1:POP_SIZE for current_trait = 1:NUM_TRAITS, trait(current_trait,pop_member,popcount)=... (rand-(1/2))*(HIGHTRAIT(current_trait)-LOWTRAIT(current_trait))+... (1/2)*(HIGHTRAIT(current_trait)+LOWTRAIT(current_trait)); % This starts the population off with numbers chosen randomly % on the allowed range of variation, for this example. end end else for pop_member = 1:POP_SIZE for current_trait = 1:NUM_TRAITS, trait(current_trait,pop_member,popcount)=0; % To start with a guess where all the population members are zero end end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % In this next loop the fitness is calculated, the children are % created and it repeats until the EPSILON-DELTA termination condition % is satisfied or MAX_GENERATION is reached. This is the main % loop of the RGA. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% while popcount <= MAX_GENERATION, %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % USER: Below is where you change the fitness function that is to be % maximized. This may involve changing several lines of code below. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % In the example below we want to *maximize* the function z=f(x,y). % Important: When defining the fitness function you must remember that % it is a function that you are *maximizing* and that it must always be % positive (the selection mechanism depends on this). For our example, % we are maximizing the function f. Below, we include some ideas about % what to do if you seek to minimize a function. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sumfitness = 0; % Re-initialize for each generation % First, determine the values of the function to be minimized for % each chromosome. for chrom_number = 1:POP_SIZE, % Test fitness fitness_bar(chrom_number)=... +5*exp(-0.1*((trait(1,chrom_number,popcount)-15)^2+... (trait(2,chrom_number,popcount)-20)^2))... -2*exp(-0.08*((trait(1,chrom_number,popcount)-20)^2+... (trait(2,chrom_number,popcount)-15)^2))... +3*exp(-0.08*((trait(1,chrom_number,popcount)-25)^2+... (trait(2,chrom_number,popcount)-10)^2))... +2*exp(-0.1*((trait(1,chrom_number,popcount)-10)^2+... (trait(2,chrom_number,popcount)-10)^2))... -2*exp(-0.5*((trait(1,chrom_number,popcount)-5)^2+... (trait(2,chrom_number,popcount)-10)^2))... -4*exp(-0.1*((trait(1,chrom_number,popcount)-15)^2+... (trait(2,chrom_number,popcount)-5)^2))... -2*exp(-0.5*((trait(1,chrom_number,popcount)-8)^2+... (trait(2,chrom_number,popcount)-25)^2))... -2*exp(-0.5*((trait(1,chrom_number,popcount)-21)^2+... (trait(2,chrom_number,popcount)-25)^2))... +2*exp(-0.5*((trait(1,chrom_number,popcount)-25)^2+... (trait(2,chrom_number,popcount)-16)^2))... +2*exp(-0.5*((trait(1,chrom_number,popcount)-5)^2+... (trait(2,chrom_number,popcount)-14)^2)); end % Next, compute the fitness function (this loop is only kept % separate from the next one in case you need to implement % some of the options discussed in the comments). for chrom_number = 1:POP_SIZE, % Test fitness % The fitness function must be chosen so that it is always positive. % To ensure this for our example we assume that we know that the value % of f will never be below -5; so we simply add 5 to every fitness_bar value. % Another approach would be to simply find the minimum value % of the fitness_bar function for every element in the population % and then add on its absolute value so that all the resulting % values will be positive fitness(chrom_number)= fitness_bar(chrom_number) + 5; % Notice that when we turn a minimization problem into a maximization % problem we often use % fitness(chrom_number)= -fitness_bar(chrom_number) + max(fitness_bar); % as the above line. Here, the minus sign is % for turning the minimization problem into a maximization problem % and max(fitness_bar) is used to make the fitness function positive. % Another option would be to use the fitness function: % fitness(chrom_number)=(1/(fitness_bar(chrom_number) + .1)); % The 1/fitness_bar function switches it to a maximzation problem. % The +.1 is to make sure that there is no divide by zero. Note that you can % tune the GA in this case by changing the 1 in the numerator to another % constant and the .1 to a different value. sumfitness = sumfitness + fitness(chrom_number); % Store this for % use below end % Next, determine the most fit and least fit chromosome and % the chrom_numbers (which we call bestmember and worstmember). [bestfitness(popcount),bestmember]=max(fitness); [worstfitness(popcount),worstmember]=min(fitness); % Next, save these (if want to save worstindividual can too) bestindividual(:,popcount)=trait(:,bestmember,popcount); %worstindividual(:,popcount)=trait(:,worstmember,popcount); % Compute the average fitness in case you want to plot it. avefitness(popcount) = sumfitness / POP_SIZE; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Create the next generation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % First, form the mating pool. % To do this we select as parents the % chromosomes that are most fit. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% for pop_member = 1:POP_SIZE, if ELITISM ==1 & pop_member==bestmember % If elitism on, and have % the elite member parent_chrom(:,pop_member)=trait(:,pop_member,popcount); % Makes sure that % the elite member gets into the next % generation. else pointer=rand*sumfitness; % This makes the pointer for the roulette % wheel. member_count=1; % Initialization total=fitness(1); while total < pointer, % This spins the wheel to the % pointer and finds the % chromosome there - which is % identified by member_count member_count=member_count+1; total=total+fitness(member_count); end % Next, make the parent chromosome parent_chrom(:,pop_member)=trait(:,member_count,popcount); end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Reproduce section (i.e., make off-spring - "children") %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % In the approach below each individual gets to mate and % they randomly pick someone else (not themselves) in the % mating pool to mate with. Resulting children are % composed of a combination of genetic material of their % parents. For the AGA crossover is different since % we have not split the numbers up into sequences of % digits. We can, however, achieve essentially the same effect as % the generation of a random cross site by the random % generation of how much of each trait will be swapped % with the corresponding trait from the mate and by % randomly selecting a trait that is crossed over. % Next, notice that if elitism is on, when the elite % member gets a chance to mate they do not; they are % simply copied over to the next generation. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% for parent_number1 = 1:POP_SIZE, % Crossover (parent_number1 is the % individual who gets to mate if ELITISM ==1 & parent_number1==bestmember % If elitism on, and % have the elite member child(:,parent_number1)=parent_chrom(:,parent_number1); else parent_number2=parent_number1; % Initialize who the mate is while parent_number2 == parent_number1 % Iterate until find % a mate other than % yourself parent_number2 = rand*POP_SIZE; % Choose parent number 2 % randomly (a random mate) parent_number2 = parent_number2-rem(parent_number2,1)+1; end if CROSS_PROB > rand % If true then crossover occurs % and here we simply then crossover % all the traits on an individual alpha=rand; % Specifies a random proportion of genetic % material that is going to be kept % by parent_number1 in mating trait_site = rand*NUM_TRAITS; % Pick the trait that % is crossed over (i.e., where the split is made trait_site = trait_site-rem(trait_site,1)+1; % The next lines form the child by the swapping of genetic % material between the parents. child(trait_site,parent_number1)=... alpha*parent_chrom(trait_site,parent_number1)+... (1-alpha)*parent_chrom(trait_site,parent_number2); % This takes part of parent_number1 (as given by % alpha) and part of parent_number2 (specified % by 1-alpha, the remaining portion) % and makes a child from this child(1:trait_site-1,parent_number1)=... parent_chrom(1:trait_site-1,parent_number1); child(trait_site:NUM_TRAITS,parent_number1)=... parent_chrom(trait_site:NUM_TRAITS,parent_number2); % These last two lines make the child have the first % part of parent1 and the last part of parent2 % In this way, at the trait site we think of randomly % splitting the two traits, and the child gets the % first traits of parent1 and the last traits of parent2 % (clearly there are many other ways to do this - here we % simply pick one way to make it analogous to the standard % GA) % Note that it is easy to modify the above code so that % all traits are crossed over in different proportions. This % would then be a "multiple crossover point" strategy. % One way to do this would be to simply let %child(:,parent_number1)=... % alpha*parent_chrom(:,parent_number1)+... % (1-alpha)*parent_chrom(:,parent_number2); % This crossover operation can result in traits that are out % of the range specified by HIGHTRAIT and LOWTRAIT so we % put them back in range here. if child(trait_site,parent_number1)>HIGHTRAIT(trait_site) % The trait has went higher than the upper % bound so let the trait equal to the % HIGHTRAIT bound. child(trait_site,parent_number1)=HIGHTRAIT(trait_site); % Now consider the other case: elseif child(trait_site,parent_number1) rand % If true then mutate % To mutate simply change the trait to any allowable % one on the search space (hence this is a "trait mutation" % rather than a "gene mutation" child(current_trait,pop_member)=... (rand-(1/2))*(HIGHTRAIT(current_trait)-LOWTRAIT(current_trait))+... (1/2)*(HIGHTRAIT(current_trait)+LOWTRAIT(current_trait)); end % End "if MUTAT_PROB > rand ... end end % End "if ELITISM..." end % End for pop_member loop %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Create the next generation (this completes the main part of the GA) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% popcount=popcount+1; % Increment to the next generation trait(:,:,popcount)=child; % Create next generation (children % become parents) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Terminate the program when the best fitness has not changed % more than EPSILON over the last DELTA generations. It would also % make sense to use avefitness rather than bestfitness in this test. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if popcount > DELTA+1 & ... max(abs(bestfitness(popcount-DELTA:popcount-1)-... bestfitness(popcount-DELTA-1:popcount-2)))<=EPSILON break; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% end % End "for pop_count=..." loop - the main loop. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Next, provide some plots of the results of the simulation. % These are for the example function that we seek to maximize; however % for other applications it is not difficult to modify this code % to show plots that are informative for the application at hand. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% t=1:popcount-1; % For use in plotting x=0:31/100:30; % For our function the range of values we are considering y=x; % Compute the function that we are trying to find the maximum of. for jj=1:length(x) for ii=1:length(y) z(ii,jj)=... +5*exp(-0.1*((x(jj)-15)^2+(y(ii)-20)^2))... -2*exp(-0.08*((x(jj)-20)^2+(y(ii)-15)^2))... +3*exp(-0.08*((x(jj)-25)^2+(y(ii)-10)^2))... +2*exp(-0.1*((x(jj)-10)^2+(y(ii)-10)^2))... -2*exp(-0.5*((x(jj)-5)^2+(y(ii)-10)^2))... -4*exp(-0.1*((x(jj)-15)^2+(y(ii)-5)^2))... -2*exp(-0.5*((x(jj)-8)^2+(y(ii)-25)^2))... -2*exp(-0.5*((x(jj)-21)^2+(y(ii)-25)^2))... +2*exp(-0.5*((x(jj)-25)^2+(y(ii)-16)^2))... +2*exp(-0.5*((x(jj)-5)^2+(y(ii)-14)^2)); end end % First, show the actual function to be maximized and its contour map figure(1) clf surf(x,y,z); colormap(jet) % Use next line for generating plots to put in black and white documents. colormap(gray); xlabel('x'); ylabel('y'); zlabel('z'); title('Fitness function'); figure(2) clf contour(x,y,z,25) colormap(jet) % Use next line for generating plots to put in black and white documents. colormap(gray); xlabel('x'); ylabel('y'); zlabel('z'); title('Fitness function (contour map)'); hold on % Next, add on the evolution of the other population % members over time. for pop_member = 1:POP_SIZE xx=squeeze(trait(1,pop_member,:)); yy=squeeze(trait(2,pop_member,:)); traitplot=plot(xx,yy,'k.'); set(traitplot,'MarkerSize',6); end % Then, add on the evolution of the best individual over % time that the GA finds. btraitplot=plot(bestindividual(1,:),bestindividual(2,:),'ko'); set(btraitplot,'MarkerSize',4) hold off % Next, plot some data on the operation of the GA figure (3) clf subplot(211) plot(t,bestfitness,'k--',t,avefitness,'k-',t,worstfitness,'k-.') xlabel('Generation') ylabel('Best, average, and worst fitness') title('Best and worst fitness vs. generation') subplot(212) plot(t,bestindividual(1,:),'k-',t,bestindividual(2,:),'k--') xlabel('Generation') ylabel('Best individuals') title('Best individuals vs. generation') %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % End of program %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%