Genetic algorithm (python version)

Genetic algorithm (python version)

1. Basic Concepts


Genetic Algorithm (GA) is an intelligent search algorithm first proposed by Professor Holland in the United States based on the basic principle of "survival of the fittest, survival of the fittest" in nature. This law is a good explanation of the natural selection process of biological evolution. Genetic algorithm also draws on this basic rule, through the idea of ​​population, transforms the solution of the problem into the individuals in the population through coding, and let these individuals continuously simulate the biological evolution process through selection, crossover and mutation operators, and then The "survival of the fittest" rule is used to select individuals with strong adaptability in the population to form a sub-population, and then let the sub-population repeat a similar evolution process until the optimal solution to the problem is found or a certain evolution (operation) time is reached.

Several important noun concepts in the Ga algorithm.

Individual (chromosome): An individual (chromosome) represents an organism in nature, and in the GA algorithm, an individual (chromosome) represents a solution to a specific problem.


Gene: In the GA algorithm, a gene represents a decision variable for a specific problem solution. The corresponding relationship between the problem solution and the gene in the chromosome is as follows:


Population: Multiple individuals form a population. In the GA algorithm, multiple sets of solutions to a problem constitute a population of solutions to the problem.

2. Main steps

The basic steps of the GA algorithm are as follows:

Step 1. Population initialization. Select a coding scheme and then initialize a certain number of individuals to form a population of GA in the solution space by means of random generation.

Step 2. Assess the population. The heuristic algorithm is used to generate a layout diagram for the individuals in the population (the order of the rectangular pieces), and the fitness function value (utilization rate) of the individual is calculated accordingly, and then the optimal individual in the current population is saved as the searched optimal individual. untie.

Step 3. Choose Action. According to the fitness of individuals in the population, individuals with high fitness are selected from the current population through roulette or expected value method.

Step 4. Crossover operation. The individual selected in the previous step is controlled by a certain probability threshold Pc whether to use single-point crossover, multi-point crossover or other crossover methods to generate new crossover individuals.

Step 5. Mutation operation. A certain probability threshold Pm is used to control whether to perform single-point mutation or multi-point mutation on some genes of an individual.

Step 6. Terminate judgment. If the termination condition is met, terminate the algorithm, otherwise return to Step 2.

The flow chart is as follows:


3. Main operation introduction

3.1 Population initialization

The initialization of the population depends on the specific problem. For example, a problem has n decision variables { x1,x2,...,xn }. Each decision variable has a range of values: the lower bound {L 1, L2,…, Ln } and the upper bound {U 1, U2,…, Un }, then the initialization of the individuals in the population is randomly within the value range of the decision variable The value of each decision variable is generated within: Xj={x1,x2,...,xn}, where xi belongs to the range (Li, Ui). All individuals constitute a population. When each individual is initialized, the population is initialized.

3.2 Evaluating populations

The evaluation of the population is to calculate the fitness value of the individuals in the population. Suppose the population population has popsize individuals. Calculate the fitness value of each individual and evaluate the population in turn.

3.3 Select Action

The common selection operation in the GA algorithm is the roulette method: the individual with better fitness value in the population has a greater probability of being selected. Assuming popsize=4, calculate the probability of being selected for each individual according to the following expression, and then use a pie chart to represent it as follows.

P(Xj) = fit(Xj)/(fit(X1)+fit(X2)+fit(X3)+fit(X4)),j=1,2,3,4


When choosing according to the roulette method, the higher the probability, the easier it is to be selected.

3.4 Crossover operation

There are also many kinds of crossover operations: one-point crossover, two-point crossover, etc. Only two points of intersection are explained here. First, use the selection operation to select two parent individuals parent1 and parent2 from the population, then randomly generate two positions pos1 and pos2, and exchange the locus information between the two positions to obtain off1 and off2 as shown in the figure below. However, the two individuals generally have conflicting loci information (integer coding), at this time, the off1 and off2 individuals need to be adjusted: the conflicting genes in off1 are adjusted according to the genes in parent1 to those in parent2 genes at the same location. If "1" in off1 appears twice, the "1" in the second place needs to be adjusted to "4" in parent2 corresponding to "1" in parent1, and so on to deal with conflicting genes in off1. It should be noted that to adjust off2, you need to refer to parent2.

3.5 Mutation operation

For mutation operations, there are different mutation operations according to different encoding methods.

If it is a floating-point encoding, the mutation can mutate (regeneration or other adjustment scheme) the information of a certain locus in the middle of the chromosome.


If an integer coding scheme is used, there are generally several mutation methods: position mutation and sign mutation.

Positional variation: 


Symbolic variation: 


4. Python code

#-*- coding:utf-8 -*-

import random
import math
from operator import itemgetter

class Gene:
    '''
    This is a class to represent individual(Gene) in GA algorithom
    each object of this class have two attribute: data, size
    '''
    def __init__(self,**data):
        self.__dict__.update(data)       
        self.size = len(data['data'])#length of gene
                                
        
class GA:
    '''
    This is a class of GA algorithm.
    '''
    def __init__(self,parameter):
        '''
        Initialize the pop of GA algorithom and evaluate the pop by computing its' fitness value .
        The data structure of pop is composed of several individuals which has the form like that:
        
        {'Gene':a object of class Gene, 'fitness': 1.02(for example)}

        Representation of Gene is a list: [b s0 u0 sita0 s1 u1 sita1 s2 u2 sita2]
        
        '''
        #parameter = [CXPB, MUTPB, NGEN, popsize, low, up]
        self.parameter = parameter

        low = self.parameter[4]
        up = self.parameter[5]
        
        self.bound = []
        self.bound.append(low)
        self.bound.append(up)
        
        pop = []
        for i in range(self.parameter[3]):
            geneinfo = []
            for pos in range(len(low)):
                geneinfo.append(random.uniform(self.bound[0][pos], self.bound[1][pos]))#initialise popluation
                
            fitness = evaluate(geneinfo)#evaluate each chromosome
            pop.append({'Gene':Gene(data = geneinfo), 'fitness':fitness})#store the chromosome and its fitness
            
        self.pop = pop
        self.bestindividual = self.selectBest(self.pop)#store the best chromosome in the population
        
    def selectBest(self, pop):
        '''
        select the best individual from pop
        '''
        s_inds = sorted(pop, key = itemgetter("fitness"), reverse = False)
        return s_inds[0]
        
    def selection(self, individuals, k):
        '''
        select two individuals from pop
        '''
        s_inds = sorted(individuals, key = itemgetter("fitness"), reverse=True)#sort the pop by the reference of 1/fitness
        sum_fits = sum(1/ind['fitness'] for ind in individuals) #sum up the 1/fitness of the whole pop
        
        chosen = []
        for i in xrange(k):
            u = random.random() * sum_fits#randomly produce a num in the range of [0, sum_fits]
            sum_ = 0
            for ind in s_inds:
                sum_ += 1/ind['fitness']#sum up the 1/fitness
                if sum_ > u:
                    #when the sum of 1/fitness is bigger than u, choose the one, which means u is in the range of [sum(1,2,...,n-1),sum(1,2,...,n)] and is time to choose the one ,namely n-th individual in the pop
                    chosen.append(ind)
                    break
        
        return chosen    


    def crossoperate(self, offspring):
        '''
        cross operation
        '''
        dim = len(offspring[0]['Gene'].data)

        geninfo1 = offspring[0]['Gene'].data#Gene's data of first offspring chosen from the selected pop
        geninfo2 = offspring[1]['Gene'].data#Gene's data of second offspring chosen from the selected pop
        
        pos1 = random.randrange(1,dim)#select a position in the range from 0 to dim-1,
        pos2 = random.randrange (1, dim)

        newoff = Gene(data = [])#offspring produced by cross operation
        temp = []
        for i in range(dim):
            if (i >= min(pos1,pos2) and i <= max(pos1,pos2)):
                temp.append(geninfo2[i])
                #the gene data of offspring produced by cross operation is from the second offspring in the range [min(pos1,pos2),max(pos1,pos2)]
            else:
                temp.append(geninfo1[i])
                #the gene data of offspring produced by cross operation is from the frist offspring in the range [min(pos1,pos2),max(pos1,pos2)]
        newoff.data = temp
       
        return newoff


    def mutation(self, crossoff, bound):
        '''
        mutation operation
        '''
        
        dim = len (crossoff.data)

        pos = random.randrange (1, dim) #chose a position in crossoff to perform mutation.

        crossoff.data[pos] = random.uniform(bound[0][pos],bound[1][pos])
        return crossoff
    
    def GA_main(self):
        '''
        main frame work of GA
        '''
        
        popsize = self.parameter[3]
        
        print("Start of evolution")
        
        # Begin the evolution
        for g in range(NGEN):
            
            print("-- Generation %i --" % g)      
                      
            #Apply selection based on their converted fitness
            selectpop = self.selection(self.pop, popsize)   

            nextoff = []    
            while len(nextoff) != popsize:      
                # Apply crossover and mutation on the offspring            
                                
                # Select two individuals
                offspring = [random.choice(selectpop) for i in xrange(2)]
                
                if random.random() < CXPB: # cross two individuals with probability CXPB
                    crossoff = self.crossoperate(offspring)
                    fit_crossoff = evaluate(self.xydata, crossoff.data)# Evaluate the individuals           
                    
                    if random.random() < MUTPB: # mutate an individual with probability MUTPB
                        muteoff = self.mutation(crossoff,self.bound)
                        fit_muteoff = evaluate(self.xydata, muteoff.data)# Evaluate the individuals
                        nextoff.append({'Gene':muteoff,'fitness':fit_muteoff})
                        
            # The population is entirely replaced by the offspring
            self.pop = nextoff
            
            # Gather all the fitnesses in one list and print the stats
            fits = [ind['fitness'] for ind in self.pop]
                
            length = len(self.pop)
            mean = sum(fits) / length
            sum2 = sum(x*x for x in fits)
            std = abs(sum2 / length - mean**2)**0.5
            best_ind = self.selectBest(self.pop)

            if best_ind['fitness'] < self.bestindividual['fitness']:
                self.bestindividual = best_ind

            print("Best individual found is %s, %s" % (self.bestindividual['Gene'].data,self.bestindividual['fitness']))
            print("  Min fitness of current pop: %s" % min(fits))
            print("  Max fitness of current pop: %s" % max(fits))
            print("  Avg fitness of current pop: %s" % mean)
            print("  Std of currrent pop: %s" % std)
        
        print("-- End of (successful) evolution --")    

if __name__ == "__main__":

    CXPB, MUTPB, NGEN, popsize = 0.8, 0.3, 50, 100#control parameters
    
    up = [64, 64, 64, 64, 64, 64, 64, 64, 64, 64]#upper range for variables
    low = [-64, -64, -64, -64, -64, -64, -64, -64, -64, -64]#lower range for variables
    parameter = [CXPB, MUTPB, NGEN, popsize, low, up]
    
    run = GA (parameter)
    run.GA_main()


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325727069&siteId=291194637