遗传算法

遗传算法学习笔记

本学习笔记介绍了一种最优化函数值的一种方法 ---- 遗传算法

在本学习笔记中会先介绍遗传算法的用处及思想 然后分部分介绍其代码实现和具体做法 最后会给出完整代码

本学习笔记有借鉴Python手把手构建遗传算法(GA)实现最优化搜索 - FINTHON的部分 谢谢大佬的博客

欢迎各位评判指正

遗传算法

用处

遗传算法广泛应用于数学建模当中 ,用于寻找最优化函数值的问题(不仅仅是局限于寻找最大值问题)

在下文中使用样例:寻找 \( y = x_1^2 + x_2^2 + x_3^2 + x_4^2  \) 的最大值 \( \forall i , x_i \in \left [ 1,30 \right ] \)

显然 当 全部值为30时 函数值取到最大值

思想

 遗传算法模仿了遗传算法中的各个过程,在一开始我们在区间内随机出一个size的种群,然后通过交叉和变异的方式产生下一代,评估每一代的个体的适应度函数,我们选择更适应的那些个体进入下一代(亦或者是那些适应度函数更优的个体拥有更大的概率进入下一代) ,在若干代的进化之下 , 就有大的概率找到最优解 / 偏优解

一些基本概念


 

遗传算法的各个组成部分详解

初始化方法

 在初始化方法当中 随机生成一个含popsize个个体的种群并获取初始种群中的各个信息

def __init__(self,parameter):
        
    #这里的parameter是一个list变量
    #parameter = [cp,mp,max_gen,popsize,low,up]
    #cp ----> cross_possbility 交叉率
    #mp ----> mutiply_possbility 变异率
    #max_gen --> max_generation 最大繁殖代数
    
        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.randint(self.bound[0][pos],
                                               self.bound[1][pos]))
        
            fitness = self.evaluate(geneinfo)
            
            #每个初始的染色体都是一个字典
            #字典里封装着一个基因的Gene类 和 适应度函数值
            pop.append({'Gene':Gene(data=geneinfo),
                        'fitness': fitness})
            
        self.pop = pop
        self.bestindividual = self.selectBest(self.pop)

评估适应度函数

也就是最终要最优化的函数 在本样例当中 fitness = \( y = x_1^2 + x_2^2 + x_3^2 + x_4^2  \)

def evaluate(self,geneinfo):
        
        x1 = geneinfo[0]
        x2 = geneinfo[1]
        x3 = geneinfo[2]
        x4 = geneinfo[3]
        
        func = x1 ** 2 +x2 ** 2 +x3 ** 2 +x4 ** 2
        
        return func

选择最优的个体

选择一个种群当中适应度函数最高的个体

def selectBest(self,pop):
        
        #将种群按照适应度函数fitness函数从大到小排序
        #并将最大适应度函数的个体返回
        
        s_inds = sorted(pop,
                        key=itemgetter("fitness"),
                        reverse=True)
        
        return s_inds[0]

选择个体

按照轮盘赌的方式来选择个体

使用random.random()生成一个0-1之间的数字 

random.random() * fit_sum 计算出该值位于某个个体的概率区间

可以看出个体适应度越大的个体被选中的概率也就越大

def selection(self,individuals,k):
        
        s_inds = sorted(individuals,
                        key=itemgetter("fitness"),
                        reverse=True)
        sum_fits = sum(ind['fitness'] for ind in individuals)
        
        chosen = []
        
        for i in range(k):
            
            #random.random()返回一个0-1之间的值
            #我们使用轮盘赌选择的方式来进行选择
            #即u在哪个区间则选择那个区间内的个体
            #由于random.random()是均匀产生
            #可以知道fitness更大的值拥有更大的区间
            #则拥有更大的被选择的概率
            
            u = random.random() * sum_fits
            sum_ = 0
            
            for ind in s_inds:
                
                sum_ += ind['fitness']
                
                if sum_ >= u:
                    
                    chosen.append(ind)
                    break
        
        #按照fitness从小到大的方式排序chosen的种群
        chosen = sorted(chosen,
                        key=itemgetter("fitness"),
                        reverse = False)
        
        return chosen

交叉变异

可以选择单点位交叉变异亦或是双点位交叉变异的方式

可以看见单点位变异与双点位交叉变异均可以有效的完成变异

def crossoperate(self,offspring):
        
    
        #使用双点位交叉的方式进行交叉变异
        #在实际中双点位交叉和单点位交叉的变异程度都是可以的
        
        dim = len(offspring[0]['Gene'].data)
        
        geneinfo1 = offspring[0]['Gene'].data
        geneinfo2 = offspring[1]['Gene'].data
        
        #如果基因长度只有1 进行特判
        
        if dim == 1:
            
            pos1 = 1
            pos2 = 1
            
        else:
            
            pos1 = random.randrange(1,dim)
            pos2 = random.randrange(1,dim)
            
        newoff1 = Gene(data = [])
        newoff2 = Gene(data = [])
        temp1 = []
        temp2 = []
        
        for i in range(dim):
            
            if min(pos1 , pos2) <= i <= max(pos1 , pos2):
                
                temp1.append(geneinfo1[i])
                temp2.append(geneinfo2[i])
                
            else:
                
                temp1.append(geneinfo2[i])
                temp2.append(geneinfo1[i])
        
        newoff1.data = temp1
        newoff2.data = temp2
        
        return newoff1,newoff2

变异

本文提供两种变异方式 : 逆位变异 和 基本位变异

逆位变异:将单条染色体中的两个基本位进行互换

基本位变异:将某个染色体中的某个位置进行随机变换

其解决了仅交叉变异时变异数不足导致的基因型丰富度不足的问题

 def mutation(self,crossoff,bound):
        
        #在这里使用基本位单点变异
        
        dim = len(crossoff.data)
        
        if dim == 1:
            
            pos = 0
            
        else:
            
            pos = random.randrange(0,dim)
        
        crossoff.data[pos] = random.randint(bound[0][pos],
                                            bound[1][pos])
        
        return crossoff
    
    '''
    
    def reversal_mutation(self,crossoff):
        
        #提供逆位变异的方法
        
        dim = len(crossoff.data)
        
        if dim == 1:
            
            pos1 = 0
            pos2 = 0
            
        else:
            
            pos1 = random.randrange(0,dim)
            pos2 = random.randrange(0,dim)
            
        #交换基因位
        bridge =  crossoff.data[pos2]
        crossoff.data[pos2] = crossoff.data[pos1]
        crossoff.data[pos1] = bridge
        
        return crossoff
    
    '''

主函数部分

在写好各部分的函数之后 编写主函数部分

 def GA_main(self):
        
        popsize = self.parameter[3]
        print("Evolution start")
        
        for g in range(self.parameter[2]):
            
            print("######## Generation {} #######".format(g))
            selectpop = self.selection(self.pop, popsize)
            nextoff = []
            
            while len(nextoff) != popsize:
                
                offspring = [selectpop.pop() for _ in range(2)]
                
                #判定是否需要交叉
                if random.random() < cp:
                    
                    crossoff1 , crossoff2 = self.crossoperate(offspring)
                    
                    #判定是否需要变异
                    if random.random() < mp:
                        
                        muteoff1 = self.mutation(crossoff1, self.bound)
                        muteoff2 = self.mutation(crossoff2, self.bound)
                        fit_muteoff1 = self.evaluate(muteoff1.data)
                        fit_muteoff2 = self.evaluate(muteoff2.data)
                        nextoff.append({'Gene':muteoff1,
                                        'fitness':fit_muteoff1})
                        nextoff.append({'Gene':muteoff2,
                                        'fitness':fit_muteoff2})
                    
                    else:
                        
                        fit_crossoff1 = self.evaluate(crossoff1.data)
                        fit_crossoff2 = self.evaluate(crossoff2.data)
                        nextoff.append({'Gene':crossoff1,
                                        'fitness':fit_crossoff1})
                        nextoff.append({'Gene':crossoff2,
                                        'fitness':fit_crossoff2})
                        
                else:
                    
                    nextoff.extend(offspring)
                    
            self.pop = nextoff
            
            fits = [ind['fitness'] for ind in self.pop]
            
            best_ind = self.selectBest(self.pop)
            
            if best_ind['fitness'] > self.bestindividual['fitness']:
                
                self.bestindividual = best_ind
            
            print("Best individual found is {}, {}".format(self.bestindividual['Gene'].data,
                                                       self.bestindividual['fitness']))
            print("Max fitness of current pop: {}".format(max(fits)))
 
    print("------ End of (successful) evolution ------")
            

最终代码

# -*- coding: utf-8 -*-
"""
Created on Wed Jan 19 20:47:18 2022

@author: Rbrq
"""

import random

from operator import itemgetter

class Gene:
    
    def __init__(self, **data):
        
        self.__dict__.update(data)
        self.size = len(data['data'])

class GA:
    
    def __init__(self,parameter):
        
    #这里的parameter是一个list变量
    #parameter = [cp,mp,max_gen,popsize,low,up]
    #cp ----> cross_possbility 交叉率
    #mp ----> mutiply_possbility 变异率
    #max_gen --> max_generation 最大繁殖代数
    
        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.randint(self.bound[0][pos],
                                               self.bound[1][pos]))
        
            fitness = self.evaluate(geneinfo)
            
            #每个初始的染色体都是一个字典
            #字典里封装着一个基因的Gene类 和 适应度函数值
            pop.append({'Gene':Gene(data=geneinfo),
                        'fitness': fitness})
            
        self.pop = pop
        self.bestindividual = self.selectBest(self.pop)
    
    def evaluate(self,geneinfo):
        
        x1 = geneinfo[0]
        x2 = geneinfo[1]
        x3 = geneinfo[2]
        x4 = geneinfo[3]
        
        func = x1 ** 2 +x2 ** 2 +x3 ** 2 +x4 ** 2
        
        return func
    
    def selectBest(self,pop):
        
        #将种群按照适应度函数fitness函数从大到小排序
        #并将最大适应度函数的个体返回
        
        s_inds = sorted(pop,
                        key=itemgetter("fitness"),
                        reverse=True)
        
        return s_inds[0]
    
    def selection(self,individuals,k):
        
        s_inds = sorted(individuals,
                        key=itemgetter("fitness"),
                        reverse=True)
        sum_fits = sum(ind['fitness'] for ind in individuals)
        
        chosen = []
        
        for i in range(k):
            
            #random.random()返回一个0-1之间的值
            #我们使用轮盘赌选择的方式来进行选择
            #即u在哪个区间则选择那个区间内的个体
            #由于random.random()是均匀产生
            #可以知道fitness更大的值拥有更大的区间
            #则拥有更大的被选择的概率
            
            u = random.random() * sum_fits
            sum_ = 0
            
            for ind in s_inds:
                
                sum_ += ind['fitness']
                
                if sum_ >= u:
                    
                    chosen.append(ind)
                    break
        
        #按照fitness从小到大的方式排序chosen的种群
        chosen = sorted(chosen,
                        key=itemgetter("fitness"),
                        reverse = False)
        
        return chosen
    
    def crossoperate(self,offspring):
        
    
        #使用双点位交叉的方式进行交叉变异
        #在实际中双点位交叉和单点位交叉的变异程度都是可以的
        
        dim = len(offspring[0]['Gene'].data)
        
        geneinfo1 = offspring[0]['Gene'].data
        geneinfo2 = offspring[1]['Gene'].data
        
        #如果基因长度只有1 进行特判
        
        if dim == 1:
            
            pos1 = 1
            pos2 = 1
            
        else:
            
            pos1 = random.randrange(1,dim)
            pos2 = random.randrange(1,dim)
            
        newoff1 = Gene(data = [])
        newoff2 = Gene(data = [])
        temp1 = []
        temp2 = []
        
        for i in range(dim):
            
            if min(pos1 , pos2) <= i <= max(pos1 , pos2):
                
                temp1.append(geneinfo1[i])
                temp2.append(geneinfo2[i])
                
            else:
                
                temp1.append(geneinfo2[i])
                temp2.append(geneinfo1[i])
        
        newoff1.data = temp1
        newoff2.data = temp2
        
        return newoff1,newoff2
    
    def mutation(self,crossoff,bound):
        
        #在这里使用基本位单点变异
        
        dim = len(crossoff.data)
        
        if dim == 1:
            
            pos = 0
            
        else:
            
            pos = random.randrange(0,dim)
        
        crossoff.data[pos] = random.randint(bound[0][pos],
                                            bound[1][pos])
        
        return crossoff
    
    '''
    
    def reversal_mutation(self,crossoff):
        
        #提供逆位变异的方法
        
        dim = len(crossoff.data)
        
        if dim == 1:
            
            pos1 = 0
            pos2 = 0
            
        else:
            
            pos1 = random.randrange(0,dim)
            pos2 = random.randrange(0,dim)
            
        #交换基因位
        bridge =  crossoff.data[pos2]
        crossoff.data[pos2] = crossoff.data[pos1]
        crossoff.data[pos1] = bridge
        
        return crossoff
    
    '''
        
    def GA_main(self):
        
        popsize = self.parameter[3]
        print("Evolution start")
        
        for g in range(self.parameter[2]):
            
            print("######## Generation {} #######".format(g))
            selectpop = self.selection(self.pop, popsize)
            nextoff = []
            
            while len(nextoff) != popsize:
                
                offspring = [selectpop.pop() for _ in range(2)]
                
                #判定是否需要交叉
                if random.random() < cp:
                    
                    crossoff1 , crossoff2 = self.crossoperate(offspring)
                    
                    #判定是否需要变异
                    if random.random() < mp:
                        
                        muteoff1 = self.mutation(crossoff1, self.bound)
                        muteoff2 = self.mutation(crossoff2, self.bound)
                        fit_muteoff1 = self.evaluate(muteoff1.data)
                        fit_muteoff2 = self.evaluate(muteoff2.data)
                        nextoff.append({'Gene':muteoff1,
                                        'fitness':fit_muteoff1})
                        nextoff.append({'Gene':muteoff2,
                                        'fitness':fit_muteoff2})
                    
                    else:
                        
                        fit_crossoff1 = self.evaluate(crossoff1.data)
                        fit_crossoff2 = self.evaluate(crossoff2.data)
                        nextoff.append({'Gene':crossoff1,
                                        'fitness':fit_crossoff1})
                        nextoff.append({'Gene':crossoff2,
                                        'fitness':fit_crossoff2})
                        
                else:
                    
                    nextoff.extend(offspring)
                    
            self.pop = nextoff
            
            fits = [ind['fitness'] for ind in self.pop]
            
            best_ind = self.selectBest(self.pop)
            
            if best_ind['fitness'] > self.bestindividual['fitness']:
                
                self.bestindividual = best_ind
            
            print("Best individual found is {}, {}".format(self.bestindividual['Gene'].data,
                                                       self.bestindividual['fitness']))
            print("Max fitness of current pop: {}".format(max(fits)))
 
    print("------ End of (successful) evolution ------")
            
if __name__ == "__main__":
    
    #在主函数中确定各个参数的值
    cp , mp, max_gen , popsize = 0.8 , 0.1 , 1000 , 100
    
    up = [30, 30, 30, 30]
    low = [1, 1, 1, 1]
    parameter = [cp,mp,max_gen,popsize,low,up]
    
    max_func = GA(parameter)
    max_func.GA_main()

 

posted @ 2022-01-19 23:51  rbrq  阅读(282)  评论(0)    收藏  举报