遗传算法-原理+实例+Python实现

基本术语

种群:是一个候选解集合,可以有交叉,变异这样的遗传操作作用于他们。
候选解:给定问题的一个可能的解。
基因:组成染色体的不可分割的构建块。经典的二元基因包含0和1。
基因位点:表示一个基因在染色体中的位置,例如,S=1011,0的基因位是3(从左往右)。
染色体:染色体是由一串基因组合而成的。染色体定义了一个特定的候选解。用二进制编码一个典型的染色体可能包含“010001”这样的内容。
变异:一个过程,其中候选解中的基因被随机改变以创建新的性状。 常用的交叉算子有实值变异和二进制变异。
交叉:两个父代个体的部分结构加以替换重组而形成新个体的操作。常用的交叉算子有实值重组和二进制交叉。最常见的交叉算子为单点交叉。如
A : 1100 1 > 11000 个体A:1100↑1->11000 新个体
B : 0011 0 > 00111 个体B:0011↑0->00111 新个体
选择:这是选择的候选解,繁殖下一代解的技术。常用的选择算子有适应度比例方法、随机遍历抽样法、局部选择法。最常用的是轮盘赌选择法,即各个体的选择概率和其适应度值成比例。设群体大小为n,其中个体i的适应度为 f i f_i ,则 i i 被选择的概率为: p i = f i f i p_{i} =\frac{ f_{i} }{ \sum{f_{i}} }
搜索空间:处理优化问题时,有多个候选解需要搜索,我们就称解的集合是搜索空间。在搜索空间中有距离的概念,距离近的解更有可能表示出相似的特征。例如”101“和”111“距离为1,”000“和”111“距离为3。
特征值:基因的特征值与二进制的权一致,例如在S=1011中,基因位置3中的1对应的基因特征值为2。
适应度:一个评分,用于评定候选解适合给定问题的程度。交叉和变异是可以看做在搜索空间中跨出一步,得到的解有可能比亲代的适应度更差。如果解表现的足够差,最终会在选择过程中从基因库删除。适应度函数指的是对问题中的每一个染色体都能够度量的函数,这个函数是计算个体在群体中被使用的概率。

参数

变异率:特定基因将要变异的概率,变异率允许种群中有更多的遗传多样性,避免局部最优。但是过高会失去原有种群中的良好解,太低则会用过长的时间在搜索空间中移动,从而妨碍其找到一个满意解的时间。例如“100”和“101”如果没有变异,其后代只能来自于交叉,只有两个后代’100’和’101’。 变异率通常取值为 [ 0.001 , 0.1 ] [0.001,0.1]
种群规模:即遗传算法中任意一代种群的个体数。较大的种群规模有利于搜素空间中的取样,但是会消耗过多计算资源,所以需要平衡。
交叉率:较高的交叉率有利于产生新的,潜在的优越的解,较低的交叉率有利于保证原种群中的良好解不受破坏。

常用遗传算法终止条件

1.到达世代的最大数目
2.超过分配给它的时间
3.发现一个满足所需条件的解
4.该算法到了一个稳定阶段

算法实现过程

Created with Raphaël 2.2.0 初始化种群 评估 终止? 结果 选择 交叉 变异 yes no

1.初始化种群:随机提供整个搜索空间的均匀覆盖。
2.评估:为种群中的每一个个体分配一个适应度值,对种群进行评估,在这个阶段我们要注意当前最优解及种群的平均适应度。
3.判断:根据终止条件集判断是否该终止搜索。如果终止条件不满足,则转入下一个阶段。
4.选择:种群经过一个选择阶段,基于适应度评分,从种群中选择个体,适应度越高,个体就更有机会被选择。
5.对选择的个体进行交叉和变异,为下一代创建新的个体。
6.返回到评估,对当前种群重新评估,我们称以上过程为一个世代。预设代数一般为100-500代。

伪代码

  generation = 0;	
  population[generation] = initializePopulation(populationSize);
  evalutePopulation(population[generation]); 	     	
  while isTerminatationConditionMet() == false do	
       parents = selectParents(population[generation]);	
       population[generation + 1] = crossover(parents);
       population[generation + 1] = mutate(population[generation + 1]);
       evaluatePopulation(population[generation]); 	               	
       generation + +;
  EndLoop

实例+Python实现

问题:求下列函数最大值: f ( x ) = 9 s i n ( 5 x ) + 8 c o s ( 4 x ) , x [ 0 , 15 ] f(x) = 9sin(5x)+8cos(4x),其中x\in[0,15]

import math
import random
import numpy as np
import matplotlib.pyplot as pltclass Population(object):
   # 初始化种群,num为每个种群的个体数量,chromlength为二进制编码长度
      # left为定义域的左边界,right为定义域的右边界
   def __init__(self, num, chromlength, left, right):
      #empty返回一个指定大小的空矩阵,并以0或者1填满
      pop = np.empty((num, chromlength),
                  dtype=np.uint)
      for i in range(num):
         for j in range(len(pop[0, :])):
            pop[i, j] = random.randint(0, 1)
      self.pop = pop
      self.left = left
      self.right = right      # 定义种群二进制解码方式
   def decodex(self):
      # pop1用来将pop中的二进制数转换成十进制数,方便解码
      pop1 = np.empty_like(self.pop)
      for i in range(len(pop1[0, :])):
         for j in range(len(pop1[:, 0])):
            pop1[j, i] = self.pop[j, i] * math.pow(2, 9 - i)
      # pop2用来统计pop1每行之和,pop[]表示行数列数,这里pop1表示的是基因位对应的特征值
      pop2 = []
      for i in range(len(pop1[:, 0])):
         pop2.append(np.sum(pop1[i, :]))
      pop2 = np.asarray(pop2, dtype=np.float)
      # x将二值域中的数转换为变量域中的数,并计算目标函数值,pop2算的是行的指数和,即每条染色体的特征值和
      #enumerate返回的是索引和对应列表值
      for i, x in enumerate(pop2):
         x = x * (self.right - self.left) / (math.pow(2, len(self.pop[0, :])) - 1)
         pop2[i] = x
      return pop2      # 解码后的相应函数值
   def decodey(self):
      pop = np.empty_like(self.decodex(),
                     dtype=np.float)
      for i, x in enumerate(self.decodex()):
      # 这里算的是各个染色体对应的函数值(带入)
         pop[i] = 9 * math.sin(5 * x) + 8 * math.cos(4 * x)
      return pop      # 计算群体中每个个体的适应度
   def fitness(self):
      fit = []
      # 适应度映射函数设为每个值减去群体中的最小值
      for individual in self.decodey():
         fit.append(individual -
                  np.min(self.decodey()))
      fit = np.asarray(fit, dtype=np.float)
      return fit   # 种群个体的选择复制,采用轮盘赌选择法选择
   def selection(self):
      total = np.sum(self.fitness())  # 求适应度之和
      # 防止种群内每个个体适应度一样使得total为0
      if total == 0:
         total = 1
      pfit = self.fitness() / total  # 单个个体被选择的概率
      pfit = np.cumsum(pfit)  # 个体的累计概率?这里不覆盖吗
      ps = []
      # 生成0-1的随机数,用来与个体被选择的概率进行比较
      for i in range(len(pfit)):
         ps.append(random.random())
      ps.sort()
      ps = np.asarray(ps)
      # 生成新的种群
      newpop = []
      fitin = 0
      newin = 0
      while newin < len(pfit):
         if ps[newin] < pfit[fitin]:
      # copy.copy是浅拷贝,子对象不会拷贝,为原对象,即被选择概率比随机概率大的染色体我们就将其拷贝为新种群
            newpop.append(copy.copy(self.pop
                              [fitin, :]))
            newin += 1
         else:
      # 略过某行种群
            fitin += 1
      self.pop = np.asarray(newpop, dtype=np.uint)      # 种群个体的交叉
   def crossover(self, pc):
      # 生成新的种群
      newpop = np.empty_like(self.pop)
      for i in range(0, len(self.pop[:, 0]), 2):
      # 如果随机概率小于发生交叉的概率,则随机选择一段进行交叉,左交叉点和右交叉点
         if random.random() < pc:
            le = random.randint(0, len(self.pop[0, :]))
            ri = random.randint(0, len(self.pop[0, :]))
            if ri <= le:
               le, ri = ri, le
            newpop[i, :le] = copy.copy(self.pop
                                 [i, :le])
            newpop[i, le:ri] = copy.copy(self.pop
                                  [i + 1, le:ri])
            newpop[i, ri:] = copy.copy(self.pop
                                 [i, ri:])
      # 以上构成了第一段交叉新个体,下面为第二段新个体
            newpop[i + 1, :le] = copy.copy(self.pop
                                    [i + 1, :le])
            newpop[i + 1, le:ri] = copy.copy(self.pop
                                     [i, le:ri])
            newpop[i + 1, ri:] = copy.copy(self.pop
                                    [i + 1, ri:])
         else:
            newpop[i, :] = copy.copy(self.pop
                               [i, :])
            newpop[i + 1, :] = copy.copy(self.pop
                                  [i + 1, :])
      self.pop = newpop   # 种群个体的变异
   def mutation(self, pm):
      for i in range(len(self.pop[:, 0])):
         # 如果随机概率小于发生变异的概率,则随机选择一点进行变异
         if random.random() < pm:
   # point指代一条染色体中的某个位置
            point = random.randint(0, len(self.pop
                                   [0, :]) - 1)
            self.pop[i, point] = (self.pop[i, point] + 1) % 2
         else:
            pass
if __name__ == '__main__':
    p=Population(20,10,left=0,right=15)#随机产生初始化群体
    x0=[]#设置横坐标点
    y0=[]#设置纵坐标点
    for i in range(20):#设置迭代次数为20次
        fitness=p.fitness()#计算群体中每个个体的适应度
        #将最大适应度的点添加进x列表和y列表
        x0.append(p.decodex()[np.argmax(fitness)])
    y0.append(p.decodey()[np.argmax(fitness)])
    p.selection()#进行选择复制操作
    p.crossover(pc=0.7)#进行交叉操作,交叉概率为0.7
    p.mutation(pm=0.1)#进行变异操作,变异概率为0.1
    print(max(y0))
    # 下面为画图操作
    x=np.arange(0.,15.,0.0001)
    y=np.empty_like(x)
    for i,v in enumerate(x):
        v0=9*math.sin(5*v)+8*math.cos(4*v)
        y[i]=v0
    plt.plot(x,y,label='aimfunction')
    plt.plot(x0,y0,'*')
    plt.legend(loc=3)
    plt.axis([0,15,-18,18])
    plt.show()```
发布了101 篇原创文章 · 获赞 46 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_40539952/article/details/102328234