Python:遗传算法实现

关于遗传算法

遗传算法是仿照自然界中生物进化而产生的一类优化算法。个人感觉遗传算法简单粗暴,适应性广。关于遗传算法的介绍网上有很多了,这里按照我自己的理解简单概括一下。

  1. 编码解码,将待优化的参数编码为DNA序列,最简单直接的为二进制编码(即有两种碱基的DNA链);
  2. 生成随机初代
  3. 选择,适应度(由待优化的模型得到)较好的个体有更大的概率被选择,应用比较多的方法有轮盘赌和锦标赛;
  4. 按照一定概率进行随机的交叉变异
  5. GOTO Step 2

经过多个世代的迭代之后,将会收敛到最优解。交叉和编译的作用是产生新个体,避免陷入局部最优解。

利用Python实现

前辈们常说一句话“避免重复造轮子”,其实最直接的还是搜一下别人写的包。这里之所以花时间自己搞一个主要是因为这个算法比较简单,逻辑性很明确,比较适合练手,因此才决定自己实现一下,算是敲开Python大门的第一个项目。

编码解码

这里选择使用二进制编码的方式来实现,根据用户输入的参数范围和精度计算出每个参数需要的位数,然后将参数空间均分映射为二进制编码。

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Encode parameters into DNA    
def encode(self, parameters):
dna = ''
for i in range(self.nGenes):
geneSequenceDigit = (parameters[i]-self.parametersRange[i,0])/(self.parametersRange[i,1]-self.parametersRange[i,0])*(2**self.geneLength[i]-1)
geneSequenceDigit = int(round(geneSequenceDigit))
geneSequenceBin = self.int2bin(geneSequenceDigit, self.geneLength[i])
dna = dna + geneSequenceBin
dna = list(dna) # Trun string to list
return dna

# Decode DNA to parameters
def decode(self, dna):
dna = ''.join(dna) # Trun list to string
parameters = []
for i in range(self.nGenes):
geneSequenceBin = dna[self.dnaIdx[i,0]:self.dnaIdx[i,1]+1]
geneSequenceDigit = self.bin2int(geneSequenceBin)
parameterI = geneSequenceDigit/(2**self.geneLength[i]-1)*(self.parametersRange[i,1]-self.parametersRange[i,0])+self.parametersRange[i,0]
parameters.append(parameterI)
return parameters

# Returns the binary string of integer n, using count number of digits
def int2bin(self, n, count=32):
binStr = "".join([str((n >> y) & 1) for y in range(count-1, -1, -1)])
return binStr

# Returns digit integer of binary string
def bin2int(self, n):
ret = int(n,2)
return ret

这种方式实现的精度并不是确切的为用户输入的精度,而是要高于用户的输入精度。

选择

选择的策略使用了名为锦标赛的方式,同时添加了精英保留机制。锦标赛是指随机选择N个(通常N=2)候选个体,再从中选择最优的个体进入下一代,重复多次,直到子代规模达到要求。精英保留机制是是指保护已经产生的最优个体不被淘汰,不被交叉和变异破坏。

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Select individuals
def select(self):
nCandidates = 2
generation = []
for i in range(self.popGeneration):
candidates = random.choices(self.generation, k=nCandidates)
fitnessList = self.fitness(candidates=candidates)
bestFitness = max(fitnessList)
idx = fitnessList.index(bestFitness)
generation.append(candidates[idx].copy())
self.generation = generation
return

# Elitist preservation
def elitistPreservation(self):
bestFitness = max(self.fitnessList)
if bestFitness > self.bestFitness:
idx = self.fitnessList.index(bestFitness)
self.bestFitness = bestFitness
self.bestIndividual = self.generation[idx].copy()
else:
worstIndividual = min(self.fitnessList)
idx = self.fitnessList.index(worstIndividual)
self.generation[idx] = self.bestIndividual.copy()
self.fitnessList[idx] = self.bestFitness
return

交叉和变异

交叉和编译比较简单,利用随机数的方式来控制发生的概率。这里值得一提的是,现实中可能会发生多点的变异或者交叉,只不过概率非常低,在遗传算法的实现中如果仅仅采用单点的交叉和变异也是可以的。

代码和例子

https://yingnan.me/files/GeneticAlgorithm.py

使用例程:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import GeneticAlgorithm as GA # 导入GeneticAlgorithm模块
import math

# 根据具体的优化目标设计适应度函数,par为输入参数的列表,ret为适应度
def fitness(par):
x = par[0]
ret = x*math.sin(10*math.pi*x)+2
return ret

maxGen = 500 # 迭代次数
popGeneration = 50 # 每个世代的个体数目
pCross = 0.1 # 发生交叉的概率
pMutation = 0.05 # 发生变异的概率
nGenes = 1 # 待优化的参数的个数
parametersRange = [[-1, 2, 0.01]] # 待优化参数的范围及要求的精度,多个参数写法:[[-1, 2, 0.01],[-2, 3, 0.1]]

# 创建GA对象
ga = GA.GeneticAlgorithm(maxGen, popGeneration, pCross, pMutation, nGenes, parametersRange, fitness)
# 显示对象信息
ga.info()
# 执行优化
ga.run()
# 显示最优解
ga.result()

转载于:https://www.cnblogs.com/ynnie/p/10590942.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值