基本概念
遗传算法是根据达尔文的“适者生存,优胜劣汰”的思想来找到最优解的额,其特点是所找到的解是全局最优解,相对于蚁群算法可能出现的局部最优解还是有优势的。
它模拟自然选择和自然遗传过程中发生的繁殖、交叉和基因突变现象,在每次迭代中都保留一组候选解,并按某种指标从解群中选取较优的个体,利用遗传算子(选择、交叉和变异)对这些个体进行组合,产生新一代的候选解群,重复此过程,直到满足某种收敛指标为止。
下面是一些基本的生物学概念,简单了解一下即可。
种群(Population):生物的进化以群体的形式进行,这样的一个群体称为种群。
个体:组成种群的单个生物。
基因 ( Gene ) :一个遗传因子。
染色体 ( Chromosome ) :包含一组的基因。
生存竞争,适者生存:对环境适应度高的、牛B的个体参与繁殖的机会比较多,后代就会越来越多。适应度低的个体参与繁殖的机会比较少,后代就会越来越少。
遗传与变异:新个体会遗传父母双方各一部分的基因,同时有一定的概率发生基因变异。
三.主要步骤
1)种群初始化。我们需要首先通过随机生成的方式来创造一个种群,一般该种群的数量为100~500,这里我们采用二进制将一个染色体(解)编码为基因型。随后用进制转化,将二进制的基因型转化成十进制的表现型。
2)适应度计算(种群评估)。这里我们直接将目标函数值作为个体的适应度。
3)选择(复制)操作。根据种群中个体的适应度大小,通过轮盘赌等方式将适应度高的个体从当前种群中选择出来。其中轮盘赌即是与适应度成正比的概率来确定各个个体遗传到下一代群体中的数量。
具体步骤如下:
(1)首先计算出所有个体的适应度总和Σfi。
(2)其次计算出每个个体的相对适应度大小fi/Σfi,类似于softmax。
(3)再产生一个0到1之间的随机数,依据随机数出现在上述哪个概率区域内来确定各个个体被选中的次数。
4)交叉(交配)运算。该步骤是遗传算法中产生新的个体的主要操作过程,它用一定的交配概率阈值(pc,一般是0.4到0.99)来控制是否采取单点交叉,多点交叉等方式生成新的交叉个体。
具体步骤如下:
(1)先对群体随机配对。
(2)再随机设定交叉点的位置。
(3)再互换配对染色体间的部分基因。
5)变异运算。该步骤是产生新的个体的另一种操作。一般先随机产生变异点,再根据变异概率阈值(pm,一般是0.0001到0.1)将变异点的原有基因取反。
6)终止判断。如果满足条件(迭代次数,一般是200~500)则终止算法,否则返回step2。
完整代码:
# -*-coding:utf-8 -*-
#目标求解2*sin(x)+cos(x)最大值
import random
import math
import numpy as np
import matplotlib.pyplot as plt
'''
chromosome_length 基因长度
population 种群
population_size 种群大小
max_value 理论上每个基因型的表现型的最大值
'''
if 1:
#初始化生成chromosome_length大小的population_size个个体的二进制基因型种群
def species_origin(population_size,chromosome_length):
population=[[]]
#二维列表,包含染色体和基因
for i in range(population_size):
temporary=[]
#染色体暂存器
for j in range(chromosome_length):
temporary.append(random.randint(0,1))
#随机产生一个染色体,由二进制数组成
population.append(temporary)
#将染色体添加到种群中
return population[1:]
# 将种群返回,种群是个二维数组,个体和染色体两维
#从二进制到十进制
#input:种群,染色体长度
def translation(population,chromosome_length):
temporary=[]
for i in range(len(population)):
total=0
for j in range(chromosome_length):
total+=population[i][j]*(math.pow(2,j))
#从第一个基因开始,每位对2求幂,再求和
# 如:0101 转成十进制为:1 * 20 + 0 * 21 + 1 * 22 + 0 * 23 = 1 + 0 + 4 + 0 = 5
temporary.append(total)
#一个染色体编码完成,由一个二进制数编码为一个十进制数
return temporary
# 返回种群中所有个体编码完成后的十进制数
'''
目标函数,一般为想要的效果,比如最大值,最小值,误差最小值等
'''
# 目标函数相当于环境 对染色体进行筛选,这里是2*sin(x)+cos(x)
def function(population,chromosome_length,max_value):
temporary=[]
function1=[]
x_show = []
temporary=translation(population,chromosome_length)
# 暂存种群中的所有的染色体(十进制)
for i in range(len(temporary)):
x=temporary[i]*max_value/(math.pow(2,chromosome_length)-1)
#一个基因代表一个决策变量,其算法是先转化成十进制,然后再除以2的基因个数次方减1(固定值)。
function1.append(2*math.sin(3*x)+math.cos(7*x))
x_show.append(x)
#这里将2*sin(x)+cos(x)作为目标函数,也是适应度函数
return function1,x_show
'''
只保留非负值的适应度/函数值(不小于0)
min_fitness 最小的适应度,对于界限值的容忍度是>=0的值,比如1时,界限值为0,-0.9也是适应的,防止淘汰过重
'''
def fitness(function1):
fitness1=[]
min_fitness=mf=1
fitness1_max = 0
for i in range(len(function1)):
if function1[i]>fitness1_max:
fitness1_max = function1[i]
for i in range(len(function1)):
if function1[i]+mf>=0:
temporary=function1[i]
else:
temporary=0.0
# 如果适应度小于0,则定为0
fitness1.append(temporary)
#将适应度添加到列表中
return fitness1
# 计算适应度和
'''
计算种群的适应度总和,为每个个体适应度占比做准备
'''
def sum(fitness1):
total = 0
for i in range(len(fitness1)):
total += fitness1[i]
return total
# 计算适应度斐波纳挈列表,这里是为了求出累积的适应度
'''
目的是形成按适应度分布的轮盘[0.1,0.4,0.5,0.0] -> [0.1, 0.5, 1.0, 1]
'''
def cumsum(fitness1):
for i in range(len(fitness1) - 2, -1, -1):
# range(start,stop,[step])
# 倒计数
total = 0
j = 0
while (j <= i):
total += fitness1[j]
j += 1
# 这里是为了将适应度划分成区间
fitness1[i] = total
fitness1[len(fitness1) - 1] = 1
# 3.选择种群中个体适应度最大的个体
'''
new_fitness 单个个体占种群的适应度占比,这可能导致某个更优的个体生存不了
new_pop 经过选择后,选中的留下,未选中的会被选中的替换掉,这意味着某个体适应度越大,新的种群中该个体数量越多
'''
def selection(population, fitness1):
new_fitness = []
# 单个公式暂存器
total_fitness = sum(fitness1)
# 将所有的适应度求和
for i in range(len(fitness1)):
new_fitness.append(fitness1[i] / total_fitness)
# 将所有个体的适应度概率化,类似于softmax
'为下一步轮盘赌做准备'
cumsum(new_fitness)
# 将所有个体的适应度划分成区间
ms = []
# 存活的种群
population_length = pop_len = len(population)
# 求出种群长度
# 根据随机数确定哪几个能存活
for i in range(pop_len):
ms.append(random.random())
# 产生种群个数的随机值
ms.sort()
# 存活的种群排序
fitin = 0
newin = 0
new_population = new_pop = population
# 轮盘赌方式
while newin < pop_len:
if (ms[newin] < new_fitness[fitin]):
new_pop[newin] = population[fitin]
newin += 1
else:
fitin += 1
population = new_pop
'交叉: 相邻两个个体随机基因位置开始交叉产生新的两个交叉个体'
def crossover(population, pc):
# pc是概率阈值,选择单点交叉还是多点交叉,生成新的交叉个体,这里没用
pop_len = len(population)
for i in range(pop_len - 1):
cpoint = random.randint(0, len(population[0]))
# 在种群个数内随机生成单点交叉点
temporary1 = []
temporary2 = []
temporary1.extend(population[i][0:cpoint])
temporary1.extend(population[i + 1][cpoint:len(population[i])])
# 将tmporary1作为暂存器,暂时存放第i个染色体中的前0到cpoint个基因,
# 然后再把第i+1个染色体中的后cpoint到第i个染色体中的基因个数,补充到temporary2后面
temporary2.extend(population[i + 1][0:cpoint])
temporary2.extend(population[i][cpoint:len(population[i])])
# 将tmporary2作为暂存器,暂时存放第i+1个染色体中的前0到cpoint个基因,
# 然后再把第i个染色体中的后cpoint到第i个染色体中的基因个数,补充到temporary2后面
population[i] = temporary1
population[i + 1] = temporary2
# 第i个染色体和第i+1个染色体基因重组/交叉完成
# step4:突变
'每个个体的先概率性突变,再随机基因点突变'
def mutation(population, pm):
# pm是概率阈值
px = len(population)
# 求出种群中所有种群/个体的个数
py = len(population[0])
# 染色体/个体中基因的个数
for i in range(px):
if (random.random() < pm):
# 如果小于阈值就变异
mpoint = random.randint(0, py - 1)
# 生成0到py-1的随机数
if (population[i][mpoint] == 1):
# 将mpoint个基因进行单点随机变异,变为0或者1
population[i][mpoint] = 0
else:
population[i][mpoint] = 1
# 将每一个染色体都转化成十进制 max_value为基因最大值,为了后面画图用
def b2d(b, max_value, chromosome_length):
total = 0
for i in range(len(b)):
total = total + b[i] * math.pow(2, i)
# 从第一位开始,每一位对2求幂,然后求和,得到十进制数?
total = total * max_value / (math.pow(2, chromosome_length) - 1)
return total
# 寻找最好的适应度和个体
def best(population, fitness1):
px = len(population)
bestindividual = []
bestfitness = fitness1[0]
for i in range(1, px):
# 循环找出最大的适应度,适应度最大的也就是最好的个体
if (fitness1[i] > bestfitness):
bestfitness = fitness1[i]
bestindividual = population[i]
return [bestindividual, bestfitness]
population_size = 500
max_value = 6
# 基因中允许出现的最大值
chromosome_length = 10
pc = 0.6
pm = 0.01
results = [[]]
fitness1 = []
fitmean = []
plt.figure(0)
population = pop = species_origin(population_size, chromosome_length)
# 生成一个初始的种群
def fun(x):
return 2*np.sin(3*x)+np.cos(7*x)
x = [i for i in np.arange(0,6,0.1)]
y = fun(np.array(x))
for i in range(population_size): # 注意这里是迭代500次
function1,position_show = function(population, chromosome_length, max_value)
plt.clf()
plt.plot(x, y)
plt.plot(position_show, function1,'bo')
print(i,len(position_show),position_show)
plt.pause(0.01) # 暂停一秒
fitness1 = fitness(function1)
best_individual, best_fitness = best(population, fitness1)
results.append([best_fitness, b2d(best_individual, max_value, chromosome_length)])
# 将最好的个体和最好的适应度保存,并将最好的个体转成十进制
selection(population, fitness1) # 选择
crossover(population, pc) # 交配
mutation(population, pm) # 变异
plt.figure(1)
results = results[1:]
results.sort()
X = []
Y = []
for i in range(500): # 500轮的结果
X.append(i)
Y.append(results[i][0])
plt.plot(X, Y)
plt.show()