Python实现简单遗传算法(SGA)


Posted in Python onJanuary 29, 2018

本文用Python3完整实现了简单遗传算法(SGA)

Simple Genetic Alogrithm是模拟生物进化过程而提出的一种优化算法。SGA采用随机导向搜索全局最优解或者说近似全局最优解。传统的爬山算法(例如梯度下降,牛顿法)一次只优化一个解,并且对于多峰的目标函数很容易陷入局部最优解,而SGA算法一次优化一个种群(即一次优化多个解),SGA比传统的爬山算法更容易收敛到全局最优解或者近似全局最优解。
SGA基本流程如下:

1、对问题的解进行二进制编码。编码涉及精度的问题,在本例中精度delta=0.0001,根据决策变量的上下界确定对应此决策变量的染色体基因的长度(m)。假设一个决策变量x0上界为upper,下界为lower,则精度delta = (upper-lower)/2^m-1。如果已知决策变量边界和编码精度,那么可以用下面的公式确定编码决策变量x0所对应的染色体长度:

2^(length-1)<(upper-lower)/delta<=2^length-1

2、对染色体解码得到表现形:

解码后得到10进制的值;decoded = lower + binary2demical(chromosome)*delta。其中binary2demical为二进制转10进制的函数,在代码中有实现,chromosome是编码后的染色体。

3、确定初始种群,初始种群随机生成

4、根据解码函数得到初始种群的10进制表现型的值

5、确定适应度函数,对于求最大值最小值问题,一般适应度函数就是目标函数。根据适应度函数确定每个个体的适应度值Fi=FitnessFunction(individual);然后确定每个个体被选择的概率Pi=Fi/sum(Fi),sum(Fi)代表所有个体适应度之和。

6、根据轮盘赌选择算子,选取适应度较大的个体。一次选取一个个体,选取n次,得到新的种群population

7、确定交叉概率Pc,对上一步得到的种群进行单点交叉。每次交叉点的位置随机。

8、确定变异概率Pm,假设种群大小为10,每个个体染色体编码长度为33,则一共有330个基因位,则变异的基因位数是330*Pm。接下来,要确定是那个染色体中哪个位置的基因发生了变异。将330按照10进制序号进行编码即从0,1,2,.......229。随机从330个数中选择330*Pm个数,假设其中一个数时154,chromosomeIndex = 154/33 =4,
geneIndex = 154%33 = 22。由此确定了第154号位置的基因位于第4个染色体的第22个位置上,将此位置的基因值置反完成基本位变异操作。

9、以上步骤完成了一次迭代的所有操作。接下就是评估的过程。对变异后得到的最终的种群进行解码,利用解码值求得每个个体的适应度值,将最大的适应度值保存下来,对应的解码后的决策变量的值也保存下来。

10、根据迭代次数,假设是500次,重复执行1-9的步骤,最终得到是一个500个数值的最优适应度取值的数组以及一个500*n的决策变量取值数组(假设有n个决策变量)。从500个值中找到最优的一个(最大或者最小,根据定义的适应度函数来选择)以及对应的决策变量的取值。
对于以上流程不是很清楚的地方,在代码中有详细的注释。也可以自行查找资料补充理论。本文重点是实现
本代码实现的问题是: maxf(x1,x2) = 21.5+x1*sin(4*pi*x1)+x2*sin(20*pi*x2)
                         s.t. -3.0<=x1<=12.1
4.1<=x2<=5.8

初始种群的编码结果如下图所示:

Python实现简单遗传算法(SGA)

初始种群的解码结果如下图所示:

Python实现简单遗传算法(SGA)

适应度值如图所示:

Python实现简单遗传算法(SGA)

轮盘赌选择后的种群如图所示;

Python实现简单遗传算法(SGA)

单点交叉后的种群如图所示:

Python实现简单遗传算法(SGA)

基本位变异后的种群如图所示;

Python实现简单遗传算法(SGA)

最终结果如下图所示;

Python实现简单遗传算法(SGA)

源代码如下;

# !/usr/bin/env python 
# -*- coding:utf-8 -*- 
# Author: wsw 
# 简单实现SGA算法 
import numpy as np 
from scipy.optimize import fsolve, basinhopping 
import random 
import timeit 
 
 
# 根据解的精度确定染色体(chromosome)的长度 
# 需要根据决策变量的上下边界来确定 
def getEncodedLength(delta=0.0001, boundarylist=[]): 
 # 每个变量的编码长度 
 lengths = [] 
 for i in boundarylist: 
  lower = i[0] 
  upper = i[1] 
  # lamnda 代表匿名函数f(x)=0,50代表搜索的初始解 
  res = fsolve(lambda x: ((upper - lower) * 1 / delta) - 2 ** x - 1, 50) 
  length = int(np.floor(res[0])) 
  lengths.append(length) 
 return lengths 
 pass 
 
 
# 随机生成初始编码种群 
def getIntialPopulation(encodelength, populationSize): 
 # 随机化初始种群为0 
 chromosomes = np.zeros((populationSize, sum(encodelength)), dtype=np.uint8) 
 for i in range(populationSize): 
  chromosomes[i, :] = np.random.randint(0, 2, sum(encodelength)) 
 # print('chromosomes shape:', chromosomes.shape) 
 return chromosomes 
 
 
# 染色体解码得到表现型的解 
def decodedChromosome(encodelength, chromosomes, boundarylist, delta=0.0001): 
 populations = chromosomes.shape[0] 
 variables = len(encodelength) 
 decodedvalues = np.zeros((populations, variables)) 
 for k, chromosome in enumerate(chromosomes): 
  chromosome = chromosome.tolist() 
  start = 0 
  for index, length in enumerate(encodelength): 
   # 将一个染色体进行拆分,得到染色体片段 
   power = length - 1 
   # 解码得到的10进制数字 
   demical = 0 
   for i in range(start, length + start): 
    demical += chromosome[i] * (2 ** power) 
    power -= 1 
   lower = boundarylist[index][0] 
   upper = boundarylist[index][1] 
   decodedvalue = lower + demical * (upper - lower) / (2 ** length - 1) 
   decodedvalues[k, index] = decodedvalue 
   # 开始去下一段染色体的编码 
   start = length 
 return decodedvalues 
 
 
# 得到个体的适应度值及每个个体被选择的累积概率 
def getFitnessValue(func, chromosomesdecoded): 
 # 得到种群规模和决策变量的个数 
 population, nums = chromosomesdecoded.shape 
 # 初始化种群的适应度值为0 
 fitnessvalues = np.zeros((population, 1)) 
 # 计算适应度值 
 for i in range(population): 
  fitnessvalues[i, 0] = func(chromosomesdecoded[i, :]) 
 # 计算每个染色体被选择的概率 
 probability = fitnessvalues / np.sum(fitnessvalues) 
 # 得到每个染色体被选中的累积概率 
 cum_probability = np.cumsum(probability) 
 return fitnessvalues, cum_probability 
 
 
# 新种群选择 
def selectNewPopulation(chromosomes, cum_probability): 
 m, n = chromosomes.shape 
 newpopulation = np.zeros((m, n), dtype=np.uint8) 
 # 随机产生M个概率值 
 randoms = np.random.rand(m) 
 for i, randoma in enumerate(randoms): 
  logical = cum_probability >= randoma 
  index = np.where(logical == 1) 
  # index是tuple,tuple中元素是ndarray 
  newpopulation[i, :] = chromosomes[index[0][0], :] 
 return newpopulation 
 pass 
 
 
# 新种群交叉 
def crossover(population, Pc=0.8): 
 """ 
 :param population: 新种群 
 :param Pc: 交叉概率默认是0.8 
 :return: 交叉后得到的新种群 
 """ 
 # 根据交叉概率计算需要进行交叉的个体个数 
 m, n = population.shape 
 numbers = np.uint8(m * Pc) 
 # 确保进行交叉的染色体个数是偶数个 
 if numbers % 2 != 0: 
  numbers += 1 
 # 交叉后得到的新种群 
 updatepopulation = np.zeros((m, n), dtype=np.uint8) 
 # 产生随机索引 
 index = random.sample(range(m), numbers) 
 # 不进行交叉的染色体进行复制 
 for i in range(m): 
  if not index.__contains__(i): 
   updatepopulation[i, :] = population[i, :] 
 # crossover 
 while len(index) > 0: 
  a = index.pop() 
  b = index.pop() 
  # 随机产生一个交叉点 
  crossoverPoint = random.sample(range(1, n), 1) 
  crossoverPoint = crossoverPoint[0] 
  # one-single-point crossover 
  updatepopulation[a, 0:crossoverPoint] = population[a, 0:crossoverPoint] 
  updatepopulation[a, crossoverPoint:] = population[b, crossoverPoint:] 
  updatepopulation[b, 0:crossoverPoint] = population[b, 0:crossoverPoint] 
  updatepopulation[b, crossoverPoint:] = population[a, crossoverPoint:] 
 return updatepopulation 
 pass 
 
 
# 染色体变异 
def mutation(population, Pm=0.01): 
 """ 
 
 :param population: 经交叉后得到的种群 
 :param Pm: 变异概率默认是0.01 
 :return: 经变异操作后的新种群 
 """ 
 updatepopulation = np.copy(population) 
 m, n = population.shape 
 # 计算需要变异的基因个数 
 gene_num = np.uint8(m * n * Pm) 
 # 将所有的基因按照序号进行10进制编码,则共有m*n个基因 
 # 随机抽取gene_num个基因进行基本位变异 
 mutationGeneIndex = random.sample(range(0, m * n), gene_num) 
 # 确定每个将要变异的基因在整个染色体中的基因座(即基因的具体位置) 
 for gene in mutationGeneIndex: 
  # 确定变异基因位于第几个染色体 
  chromosomeIndex = gene // n 
  # 确定变异基因位于当前染色体的第几个基因位 
  geneIndex = gene % n 
  # mutation 
  if updatepopulation[chromosomeIndex, geneIndex] == 0: 
   updatepopulation[chromosomeIndex, geneIndex] = 1 
  else: 
   updatepopulation[chromosomeIndex, geneIndex] = 0 
 return updatepopulation 
 pass 
 
 
# 定义适应度函数 
def fitnessFunction(): 
 return lambda x: 21.5 + x[0] * np.sin(4 * np.pi * x[0]) + x[1] * np.sin(20 * np.pi * x[1]) 
 pass 
 
 
def main(max_iter=500): 
 # 每次迭代得到的最优解 
 optimalSolutions = [] 
 optimalValues = [] 
 # 决策变量的取值范围 
 decisionVariables = [[-3.0, 12.1], [4.1, 5.8]] 
 # 得到染色体编码长度 
 lengthEncode = getEncodedLength(boundarylist=decisionVariables) 
 for iteration in range(max_iter): 
  # 得到初始种群编码 
  chromosomesEncoded = getIntialPopulation(lengthEncode, 10) 
  # 种群解码 
  decoded = decodedChromosome(lengthEncode, chromosomesEncoded, decisionVariables) 
  # 得到个体适应度值和个体的累积概率 
  evalvalues, cum_proba = getFitnessValue(fitnessFunction(), decoded) 
  # 选择新的种群 
  newpopulations = selectNewPopulation(chromosomesEncoded, cum_proba) 
  # 进行交叉操作 
  crossoverpopulation = crossover(newpopulations) 
  # mutation 
  mutationpopulation = mutation(crossoverpopulation) 
  # 将变异后的种群解码,得到每轮迭代最终的种群 
  final_decoded = decodedChromosome(lengthEncode, mutationpopulation, decisionVariables) 
  # 适应度评价 
  fitnessvalues, cum_individual_proba = getFitnessValue(fitnessFunction(), final_decoded) 
  # 搜索每次迭代的最优解,以及最优解对应的目标函数的取值 
  optimalValues.append(np.max(list(fitnessvalues))) 
  index = np.where(fitnessvalues == max(list(fitnessvalues))) 
  optimalSolutions.append(final_decoded[index[0][0], :]) 
 # 搜索最优解 
 optimalValue = np.max(optimalValues) 
 optimalIndex = np.where(optimalValues == optimalValue) 
 optimalSolution = optimalSolutions[optimalIndex[0][0]] 
 return optimalSolution, optimalValue 
 
 
solution, value = main() 
print('最优解: x1, x2') 
print(solution[0], solution[1]) 
print('最优目标函数值:', value) 
# 测量运行时间 
elapsedtime = timeit.timeit(stmt=main, number=1) 
print('Searching Time Elapsed:(S)', elapsedtime)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
打开电脑上的QQ的python代码
Feb 10 Python
线程和进程的区别及Python代码实例
Feb 04 Python
Python的socket模块源码中的一些实现要点分析
Jun 06 Python
JPype实现在python中调用JAVA的实例
Jul 19 Python
100行Python代码实现自动抢火车票(附源码)
Jan 11 Python
python爬虫爬取快手视频多线程下载功能
Feb 28 Python
python-docx修改已存在的Word文档的表格的字体格式方法
May 08 Python
关于python2 csv写入空白行的问题
Jun 22 Python
Python多继承原理与用法示例
Aug 23 Python
python selenium执行所有测试用例并生成报告的方法
Feb 13 Python
解决Django 在ForeignKey中出现 non-nullable field错误的问题
Aug 06 Python
python 利用 PIL 将数组值转成图片的实现
Apr 12 Python
Python之reload流程实例代码解析
Jan 29 #Python
Python中的默认参数实例分析
Jan 29 #Python
Python使用遗传算法解决最大流问题
Jan 29 #Python
Python subprocess模块详细解读
Jan 29 #Python
python微信跳一跳游戏辅助代码解析
Jan 29 #Python
Python面向对象之继承代码详解
Jan 29 #Python
Python多线程threading和multiprocessing模块实例解析
Jan 29 #Python
You might like
DISCUZ 分页代码
2007/01/02 PHP
php使用filter过滤器验证邮箱 ipv6地址 url验证
2013/12/25 PHP
php中file_exists函数使用详解
2015/05/08 PHP
Thinkphp实现自动验证和自动完成
2015/12/19 PHP
PHPExcel中文帮助手册|PHPExcel使用方法(分享)
2017/06/09 PHP
PHP实现的抓取小说网站内容功能示例
2019/06/27 PHP
javascript oop开发滑动(slide)菜单控件
2010/08/25 Javascript
jquery分页对象使用示例
2014/04/01 Javascript
jquery选择器排除某个DOM元素的方法(实例演示)
2014/04/25 Javascript
使用ngView配合AngularJS应用实现动画效果的方法
2015/06/19 Javascript
JavaScript动态改变div属性的实现方法
2015/07/22 Javascript
JavaScript中this详解
2015/09/01 Javascript
轻松学习jQuery插件EasyUI EasyUI创建CRUD应用
2015/11/30 Javascript
AngularJS深入探讨scope,继承结构,事件系统和生命周期
2016/11/02 Javascript
node中koa中间件机制详解
2017/08/22 Javascript
JavaScript枚举选择jquery插件代码实例
2020/11/17 jQuery
[02:39]DOTA2英雄基础教程 天怒法师
2013/11/29 DOTA
在Python中使用swapCase()方法转换大小写的教程
2015/05/20 Python
教你用python3根据关键词爬取百度百科的内容
2016/08/18 Python
利用Python-iGraph如何绘制贴吧/微博的好友关系图详解
2017/11/02 Python
python如何创建TCP服务端和客户端
2018/08/26 Python
Python函数的返回值、匿名函数lambda、filter函数、map函数、reduce函数用法实例分析
2019/12/26 Python
Pytorch 多维数组运算过程的索引处理方式
2019/12/27 Python
快速一键生成Python爬虫请求头
2021/03/04 Python
详解css3中的伪类before和after常见用法
2020/11/17 HTML / CSS
在网络中有两台主机A和B,并通过路由器和其他交换设备连接起来,已经确认物理连接正确无误,怎么来测试这两台机器是否连通?如果不通,怎么来判断故障点?怎么排
2014/01/13 面试题
学校地质灾害防治方案
2014/06/10 职场文书
反洗钱宣传活动总结
2014/08/26 职场文书
戒毒悔改检讨书
2014/09/21 职场文书
大学生在校表现评语
2014/12/31 职场文书
考试作弊检讨书范文
2015/01/27 职场文书
专业技术职务聘任证明
2015/03/02 职场文书
妇产科护理心得体会
2016/01/22 职场文书
2016年艾滋病宣传活动总结
2016/04/01 职场文书
Python基于Tkinter开发一个爬取B站直播弹幕的工具
2021/05/06 Python
canvas绘制折线路径动画实现
2021/05/12 Javascript