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

姓名:周小蓬 16019110037

转载自:http://blog.csdn.net/qq_30666517/article/details/78637255

[嵌牛导读]

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

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

[嵌牛鼻子]

遗传算法

[嵌牛提问]

什么事遗传算法,如何用python实现

[嵌牛正文]

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]view plaincopy

# !/usr/bin/env python

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

# Author: wsw

# 简单实现SGA算法

importnumpy as np

fromscipy.optimizeimportfsolve, basinhopping

importrandom

importtimeit

# 根据解的精度确定染色体(chromosome)的长度

# 需要根据决策变量的上下边界来确定

defgetEncodedLength(delta=0.0001, boundarylist=[]):

# 每个变量的编码长度

lengths = []

foriinboundarylist:

lower = i[0]

upper = i[1]

# lamnda 代表匿名函数f(x)=0,50代表搜索的初始解

res = fsolve(lambdax: ((upper - lower) *1/ delta) -2** x -1,50)

length = int(np.floor(res[0]))

lengths.append(length)

returnlengths

pass

# 随机生成初始编码种群

defgetIntialPopulation(encodelength, populationSize):

# 随机化初始种群为0

chromosomes = np.zeros((populationSize, sum(encodelength)), dtype=np.uint8)

foriinrange(populationSize):

chromosomes[i, :] = np.random.randint(0,2, sum(encodelength))

# print('chromosomes shape:', chromosomes.shape)

returnchromosomes

# 染色体解码得到表现型的解

defdecodedChromosome(encodelength, chromosomes, boundarylist, delta=0.0001):

populations = chromosomes.shape[0]

variables = len(encodelength)

decodedvalues = np.zeros((populations, variables))

fork, chromosomeinenumerate(chromosomes):

chromosome = chromosome.tolist()

start =0

forindex, lengthinenumerate(encodelength):

# 将一个染色体进行拆分,得到染色体片段

power = length -1

# 解码得到的10进制数字

demical =0

foriinrange(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

returndecodedvalues

# 得到个体的适应度值及每个个体被选择的累积概率

defgetFitnessValue(func, chromosomesdecoded):

# 得到种群规模和决策变量的个数

population, nums = chromosomesdecoded.shape

# 初始化种群的适应度值为0

fitnessvalues = np.zeros((population,1))

# 计算适应度值

foriinrange(population):

fitnessvalues[i,0] = func(chromosomesdecoded[i, :])

# 计算每个染色体被选择的概率

probability = fitnessvalues / np.sum(fitnessvalues)

# 得到每个染色体被选中的累积概率

cum_probability = np.cumsum(probability)

returnfitnessvalues, cum_probability

# 新种群选择

defselectNewPopulation(chromosomes, cum_probability):

m, n = chromosomes.shape

newpopulation = np.zeros((m, n), dtype=np.uint8)

# 随机产生M个概率值

randoms = np.random.rand(m)

fori, randomainenumerate(randoms):

logical = cum_probability >= randoma

index = np.where(logical ==1)

# index是tuple,tuple中元素是ndarray

newpopulation[i, :] = chromosomes[index[0][0], :]

returnnewpopulation

pass

# 新种群交叉

defcrossover(population, Pc=0.8):

"""

:param population: 新种群

:param Pc: 交叉概率默认是0.8

:return: 交叉后得到的新种群

"""

# 根据交叉概率计算需要进行交叉的个体个数

m, n = population.shape

numbers = np.uint8(m * Pc)

# 确保进行交叉的染色体个数是偶数个

ifnumbers %2!=0:

numbers +=1

# 交叉后得到的新种群

updatepopulation = np.zeros((m, n), dtype=np.uint8)

# 产生随机索引

index = random.sample(range(m), numbers)

# 不进行交叉的染色体进行复制

foriinrange(m):

ifnotindex.__contains__(i):

updatepopulation[i, :] = population[i, :]

# crossover

whilelen(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:]

returnupdatepopulation

pass

# 染色体变异

defmutation(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)

# 确定每个将要变异的基因在整个染色体中的基因座(即基因的具体位置)

forgeneinmutationGeneIndex:

# 确定变异基因位于第几个染色体

chromosomeIndex = gene // n

# 确定变异基因位于当前染色体的第几个基因位

geneIndex = gene % n

# mutation

ifupdatepopulation[chromosomeIndex, geneIndex] ==0:

updatepopulation[chromosomeIndex, geneIndex] =1

else:

updatepopulation[chromosomeIndex, geneIndex] =0

returnupdatepopulation

pass

# 定义适应度函数

deffitnessFunction():

returnlambdax:21.5+ x[0] * np.sin(4* np.pi * x[0]) + x[1] * np.sin(20* np.pi * x[1])

pass

defmain(max_iter=500):

# 每次迭代得到的最优解

optimalSolutions = []

optimalValues = []

# 决策变量的取值范围

decisionVariables = [[-3.0,12.1], [4.1,5.8]]

# 得到染色体编码长度

lengthEncode = getEncodedLength(boundarylist=decisionVariables)

foriterationinrange(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]]

returnoptimalSolution, 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)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,408评论 4 371
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,690评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,036评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,726评论 0 221
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,123评论 3 296
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,037评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,178评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,964评论 0 213
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,703评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,863评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,333评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,658评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,374评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,195评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,988评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,167评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,970评论 2 279

推荐阅读更多精彩内容