由于工作的需要,花了3天时间,彻底学习了一下遗传算法。遗传算法的算法思想还是比较好理解的。算法主要由四部分组成:初始化算法引擎及种群、选择、交叉和变异过程。
对于程序的结束条件,可以设置确定的迭代代数generation,也可以根据算法的收敛情况,自动结束。直接上Python代码:
# -*- coding: utf-8 -*-
from __future__ import division
import random
import math
import copy
import numpy as np
import matplotlib.pyplot as plt
class Genome:
def __init__(self):
self.genomeList = [] #一条染色体
self.fitness = 0
def init(self, chromoLength, leftPoint, rightPoint):
for idx in range(0, chromoLength):
self.genomeList.append(random.random()*(rightPoint - leftPoint) + leftPoint)
self.fitness = ObjectFunction(self.genomeList[0])
class GenAlg:
def __init__(self, popSize, chromoLength, mutationRate, crossoverRate, maxPerturbation, leftPoint, rightPoint, generation):
self.popSize = popSize #种群数量
self.chromoLength = chromoLength #基因的总数目
self.mutationRate = mutationRate #基因突变概率,一般介于0.05和0.3
self.crossoverRate = crossoverRate #基因交叉的概率一般设为0.7
self.maxPerturbation = maxPerturbation #最大变异步长
self.leftPoint = leftPoint
self.rightPoint = rightPoint
self.generation = generation #迭代代数
self.totalFitness = 0 #所有个体适应性评分的综合
self.popList = [] #初始化种群
self.allGenerationPop = [] #种群的适应值,保存了所有代的种群适应值
self.allGenerationFitResult = []
def initPop(self):
self.popList = [] #清空list
for idx in range(0, self.popSize):
genome = Genome()
genome.init(self.chromoLength, self.leftPoint, self.rightPoint)
self.popList.append(genome)
#进行选择交叉、变异,根据选择函数进行选择,然后进行交叉、变异
def chosen(self):
"""选择
输入:上一代的种群,种群的适应值
输出:下一代的种群
两代种群的数量相同,在选择新种群时,选择N(N=种群的大小)次,每次采用轮盘法,选择一条基因到下一代种群中去
适应值高的会有更大的可能性被选入下一代
"""
beChosenIdx = []
beChosen = []
#计算累计概率
fitResult = self.allGenerationFitResult[len(self.allGenerationFitResult) - 1]
totalFitness = sum(fitResult)
ratio = []
for idx in range(0, self.popSize):
ratio.append(sum(fitResult[:idx+1]) / totalFitness)
for i in range(0, self.popSize):
randValue = random.random()
for idx in range(0, self.popSize):
if ratio[idx] > randValue:
beChosenIdx.append(idx)
break
for idx in range(0, len(beChosenIdx)):
beChosen.append(self.popList[beChosenIdx[idx]])
return beChosen
def crossOver(self):
"""交叉
交叉(crossover),群体中的每个个体之间都以一定的概率 pc 交叉,即两个个体从各自字符串的某一位置
(一般是随机确定)开始互相交换,这类似生物进化过程中的基因分裂与重组。
例如,假设2个父代个体x1,x2为: x1=0100110 x2=1010001
从每个个体的第3位开始交叉,交又后得到2个新的子代个体y1,y2分别为: y1=0100001 y2=1010110
这样2个子代个体就分别具有了2个父代个体的某些特征。利用交又我们有可能由父代个体在子代组合成具有更高适合度的个体。
事实上交又是遗传算法区别于其它传统优化方法的主要特点之一。
二进制编码的交叉伪代码如下:
for idx = 0; idx <popSize; idx += 2
if rand < pc
随机生成交换位置cPoint,在cPoint之后的进制数进行交换
idx 与 idx+1 下标的染色体进行交叉
if rand > pc
不进行交叉,将idx与idx+1直接赋值给新的染色体
实数编码,如何进行交叉呢,且看下面代码?
将相邻的两个染色体进行加权
"""
#Reference http://www.docin.com/p-112399856.html
for idx in range(0, self.popSize - 1, 2): #每两组取出来一次
if (random.random() < self.crossoverRate):
crossOverPosition = random.randint(0, len(self.popList[idx].genomeList) - 1)
alpha = random.random()
beta = random.random()
genomeA = copy.deepcopy(self.popList[idx])
genomeB = copy.deepcopy(self.popList[idx + 1])
for i in range(crossOverPosition, self.chromoLength - 1):
valA = alpha * genomeA.genomeList[i] + (1 - alpha)*genomeB.genomeList[i]