基本思想
遗传算法(Genetic Algorithm, GA)
顾名思义是模仿生物的遗传学机理进行计算模拟最优解的过程。
将生物种群特征与问题进行对应
一个染色体代表问题的一个解(一个染色体含有多个基因)
一个基因代表问题的一个决策变量
多个个体构成一个种群,多组解构成解的种群。
我们使问题解的种群不断的优胜劣汰,像自然界的自然选择一般,直到最后剩下一个获胜的最优解,便结束了问题的求解。
算法流程
1. 初始化
随机生成一个种群。其中基因需要编码。
常用的编码方式有二进制编码法,浮点编码法,符号编码法。
今天先来做个二进制编码法,其解码、编码、交叉、变异等操作简单易行,但是由于随机性,局部搜索能力较差。
“与人类的染色体不同,我们假设只有“0”和“1”两种碱基,因此只需要将我们的决策变量进行二进制编码即可获得基因序列。”
2. 评估个体适应度
可以直接用函数值作为适应度,我们要找最小值嘛。
3. 进行自然选择
- 计算出适应度综合 Σ f i \Sigma f_i Σfi
- 计算出相对适应度大小 f i / Σ f i f_i/\Sigma f_i fi/Σfi
- 产生随机出,确定各个个体被选中的次数
4. 进行组合交叉
选定一个交配概率pc
- 群体随机配对
- 随机设定交叉点
- 互换染色体部分基因
5. 进行变异
- 随机产生变异点
- 根据变异概率阈值pm使原有基因突变
终止条件
可以设置若干迭代次数作为终止条件。
目标问题
emmm,是在csdn的必问上看到一个问题,要求寻找目标函数的全局最小值
就打算拿这个试一下
算法实现
初始化
首先初始化种群,如果是n维输入的话,每个染色体上都应该有n个基因,分别代表
x
1
.
.
.
.
.
x
n
x_1.....x_n
x1.....xn
这个时候需要先考虑一些如何进行二进制编码。
x的范围是-32.77到32.77,我选用13位二进制数来编码。
将一个二进制串代表的二进制数转化为十进制数:
(
b
0
.
.
.
.
.
.
b
n
)
=
(
Σ
i
=
0
n
b
i
2
i
)
=
x
′
(b_0......b_n)=(\Sigma^n_{i=0}b_i2^i)=x'
(b0......bn)=(Σi=0nbi2i)=x′
对应区间内的实数:
x
=
−
32.77
+
x
′
65.54
2
13
−
1
x = - 32.77 + x'\frac{65.54}{2^{13}-1}
x=−32.77+x′213−165.54
那么有还原的函数
def translation(L):#将二进制编码还原为变量
n = 0
for i in range(13):
n += L[i]*pow(2,i)
return -32.77 + n * (65.54/k)
适应度函数与自然选择
这里用比较简单的选择方法,每个个体被选中的概率都是自身的适应度除以总适应度的和。
α
i
=
F
i
Σ
F
\alpha_i = \frac{F_i}{\Sigma F}
αi=ΣFFi
直接使用我们的目标函数,emmm好像不太成,这样的话越小的值适应度越小了,理论上应该是适应度越大才对。
def f(x):#目标函数
n = len(x)
return -20*np.exp(-0.2*np.sqrt(1/n*sum(np.power(x,2))))\
-np.exp(1/n*sum(np.cos(2*pi*x))) + 20
不过那也好说,我们可以找出来所有个体的最大值,然后用这个值减去各个个体的函数值作为其适应度,这样最大的一个的适应度就是0,自然死亡。
def fitness(popular):
fit = [0] * len(popular)
n = len(popular[0])//13 #数据维度
maxfit = -1e5
for i in range(popular):
x = []
for j in range(n):
x.append(translation(i[j:j+13]))
fit[i] = f(np.array(x))
if fit[i] > maxfit:
maxfit = fit[i]
for i in range(len(fit)):
fit[i] = maxfit - fit[i]
return fit
接下来就是轮盘赌了,计算出所有的适应度然后根据概率判断新的种群中的个体
def choose(popular):#进行自然选择
popular_len = len(popular)#个体个数
survival_rate = []
for i in range(popular_len):
survival_rate.append(random.random())
fit = fitness(popular)#每个个体的存活率
best_individual = popular[fit.index(max(fit))]#保存下当前种群最优个体(其实写这里不太会)
survival_rate.sort()
fitin = 0
newin = 0
newpopular = popular
# 开始轮盘赌
# 结束之后,适应度更高的个体在新种群中应该更多
while newin < popular_len:
if survival_rate[newin] < fit[fitin]:
newpopular[newin] = popular[fitin]
newin += 1#旋转轮盘
else:
fitin += 1#旋转轮盘
popular = newpopular
return best_individual
交叉
取交叉率pc=0.5
def crossover(popular,pc):#pc为交叉率
popular_len=len(popular)
for i in range(popular_len-1):
crosspoint = random.randint(0,len(popular[0]))#随机生成交叉点
# 一个个体染色体长度为len(popular[0]),crosspoint为交换位置
#接下来我们让第i个个体和第i+1个个体发生交叉
t1 =[]
t2= []
t1.extend(popular[i][0:crosspoint])#i的前半段
t1.extend(popular[i+1][crosspoint:-1])#i+1的后半段
t2.extend(popular[i+1][0:crosspoint])#i+1的前半段
t2.extend(popular[i][crosspoint:-1])#i的后半段
# 放回种群
popular[i] = t1
popular[i+1] = t2
变异
取变异率0.01吧
def mutation(popular,pm):#pm为变异率
popular_len=len(popular)
chromosome_len = len(popular[0])
for i in range(popular_len):
if random.random()>pm:#大于阈值则发生变异
mutation_point = random.randint(0,chromosome_len-1)
#发生变异
if popular[i][mutation_point] == 1:
popular[i][mutation_point] = 0
else:
popular[i][mutation_point] = 1
、
最后连起来实现迭代
def GA(n):
## 初始化种群
popular_size = 100#种群大小
chromosome_length = n*13#染色体大小
popular = []
for i in range(popular_size):
individual = []
for j in range(chromosome_length):#生成随机基因
individual.append(random.randint(0,1))
popular.append(individual)#向种群添加一个个体
# 种群初始化完成
best_individual = []
for i in range(200):#迭代200次
best_individual.append( choose(popular) ) #保存下最优个体
crossover(popular,0.5)
mutation(popular,0.01)
solutions = []#解的迭代过程
for i in best_individual:
s = []
for j in range(n):
s.append(translation( i[13*j:13*j+13] ))
#还原为了十进制数
solutions.append(s)
return solutions#保存的是各次迭代发现的最优解,最后一个是结果