#data:2022-10-27
#author:斜阳
#theme:遗传算法实例
import numpy as np
import math
##############################定义参数#################################
DNA_bit = 13 # 一个DNA的二进制位数,(第一维表示符号位),长度自己指定即可,越长精度越高
Int_bit = 2 # DNA_bit-1(符号位bit)之后整数占的bit位
DNA_num = 2 # DNA的个数
animal_num = 200 # 开始种群的数量,一个种群是一个解,包含一个x和一个y
cross_rate = 0.8 # 生殖交叉概率
variation_rate = 0.005 # 变异的概率
generator_n = 50 # 种群演变的次数
limit_area = [-3, 3] # 值域
######################################定义目标函数###################################
def f(x, y):
return 3*(1-x)**2*np.exp(-(x**2)-(y+1)**2)- 10*(x/5 - x**3 - y**5)*np.exp(-x**2-y**2)- 1/3**np.exp(-(x+1)**2 - y**2)
#翻译DNA
def translate_DNA(animal): # 解码种群的DNA
def DNA2t10(DNA):#DNA二进制转换成十进制的x和y
sum = 0
# sign定义符号位,计算机中的符号位,就是在处理二进制数据时,专门规定有一位,
sign = DNA[0] # 是用来确定数据的正负,符号位是1表示负数,是0表示正数,
data = DNA[1:]# 去除符号位后面剩余的DNA,要转换成十进制
if sign == 0: # 符号位(表示二进制的正负)赋值,转换为二进制的正负号
flag = -1#flag记录程序状态
else:
flag = 1
#Int_bit DNA_bit-1 x和y整数形式的位数
for i in range(0, len(data)):#将data分成了0-int_bit-len(data)两部分
if data[i] == 1:
sum += math.pow(2, Int_bit - i - 1)
# for i in range(Int_bit, len(data)):
# if data[i] == 1:
# sum += math.pow(2, Int_bit - i - 1)
return flag * sum#正负号×十进制的数
DNA_result = []
for i in range(0, DNA_bit * DNA_num, DNA_bit):#步长为DNA_bit,0-26 两个整数
DNA = animal[i:i + DNA_bit]#13个数为一截进行赋值
translated_DNA = DNA2t10(DNA)#将13位的二进制数送进翻译函数中,转换成十进制
DNA_result.append(translated_DNA)#结果包含x和y
return DNA_result
#初始化种群************此处适合编写约束条件,根据自己要求进行编写*******************************
def flag_limit_area(animal, limit_area): # 判断种群是否符合值域,否则一票否决
x, y = translate_DNA(animal)#有一点多余,直接用x和y就可以
if x <= limit_area[1] and x >= limit_area[0] and y <= limit_area[1] and y >= limit_area[0]:
return True
else:
return False
#计算适应度
def get_fitness(animals): # 计算种群各个部分的适应度
fitness_score = np.zeros(len(animals))#生成初始的适应度分数,全为0
fit_flag = np.zeros(len(animals))#生成适应度旗帜,判断是否在区间内
for i in range(len(animals)):#len(animals)=200=种群的数量,遍历每一个种群
x, y = translate_DNA(animals[i])#二进制转换成十进制
fitness_score[i] = f(x, y)#根据目标函数计算适应度分数
# if flag_limit_area(animals[i], limit_area):#判断x和y是否在区间内,返回true和false
# fit_flag[i] = 1#标记1,表示在区间内
# else:
# fit_flag[i] = 0
fitness_score = (fitness_score - np.min(fitness_score)) + 1e-5#把负数都成非负数,且接近于0
# fitness_score = fitness_score * fit_flag # 如果不符合定义域就不取了,乘0就消失了,矩阵的乘法
fitness_p = fitness_score / (fitness_score.sum()) # 计算被选择的概率,是长度200的数组
return fitness_p
#适者生存
#numpy.random.choice(a, size=None, replace=True, p=None)
#从a(只要是ndarray都可以,但必须是一维的)中随机抽取数字,并组成指定大小(size)的数组
#replace:True表示可以取相同数字,False表示不可以取相同数字
#数组p:与数组a相对应,表示取数组a中每个元素的概率,默认为选取每个元素的概率相同。
#a = np.arange(3)
#一个参数 默认起点0,步长为1 输出:[0 1 2]
#选择操作
def select_animal(animals, fitness): # 按照适应度选择留下的种群,选择适应度高的种群,重新组成200个种群
idx = np.random.choice(np.arange(animal_num), size=animal_num, replace=True, p=(fitness)/(fitness.sum() + 1e-8))
return animals[idx]#idx是长度为200的数组
#生殖变异
def variation(children, variation_rate): # 模拟编译,输入一个种群
if np.random.rand() < variation_rate: # 以MUTATION_RATE的概率进行变异
mutate_point = np.random.randint(0, DNA_bit * 2) # 随机产生一个实数,作为变异点,代表要变异基因的位置
children[mutate_point] = children[mutate_point] ^ 1 # 这一位取反,因为只有01,所以取相反的数
# ^在算术运算中,表示异或。
# 4^3就是相当于把4化为二进制为100,
# 3化为二进制为11,
# 现在二进制100异或011,
# 异或运算中:1对1为0;1对0是1;0对1是1;0对0是0,所以100异或011就是1异或0为1,0异或1为1,0异或1为1。就是111,化为十进制2^2+2^1+1=7
return children#输入多少数量,返回多少数量,返回一个种群
#生殖交叉变异
def crossover_and_variation(animals, cross_rate): # 模拟生殖过程(包括交配和变异),输入200个种群
new_animals = []
for father in animals:#在200个种群中循环
child = father # 选择父亲,在种群中循环的下标,与子代child的下标同步
if np.random.rand() < cross_rate: # 每个种群产生子代时不是必然发生交叉,而是以一定的概率发生交叉
mother = animals[np.random.randint(animal_num)] # 再选择母亲,在200的数字中随机选择数字,作为下标
cross_points = np.random.randint(low=0, high=DNA_bit * DNA_num) # 随机产生交叉的点,在2-26中随机选择数字,作为DNA的交叉点
child[cross_points:] = mother[cross_points:] # 交叉互换,模拟生殖,从交叉点往后的片段都进行交换
variation(child, variation_rate) # 变异,当前的child进行变异,是一个种群
new_animals.append(child)#child为长度200的数组
#循环完200个种群,跳出循环
return np.array(new_animals)#np.array转换成数组形式
#查看最终答案,打印出来就结束了
def get_result(animals): # 获取结果,200个种群
fitness = get_fitness(animals)#返回的是每个种群被选择的概率,长度200的数组
max_fitness_index = np.argmax(fitness)#选择最大的选择概率的索引(下标)
print("max_fitness:", fitness[max_fitness_index])
x, y = translate_DNA(animals[max_fitness_index])
print("最优的基因型:", animals[max_fitness_index])
print("(x, y):", (x, y), f(x, y))
return
# 初始化种群(即生成初始解),需要判断开始的种群是否符合值域
# DNA_bit = 13 # 一个DNA的二进制位数,(第一维表示符号位)
# Int_bit = 2 # DNA_bit-1(符号位bit)之后整数占的bit位
# DNA_num = 2 # DNA的个数,因为解是x和y
# animal_num = 200 # 开始种群的数量
# numpy.random.randint(low, high=None, size=None, dtype=int)
###########################运行程序的起点##################################
animals = np.random.randint(2,size=(animal_num, DNA_bit * DNA_num))#随机返回0-2的animal_num*【DNA_bit * DNA_num】的数组,
# 每个animal由两个DNA组成,每个DNA为DNA_bit位
#初始化合理的种群
num = animal_num#种群数量200
while (num):#循环直到种群数为0,检查所有的种群是否在要求区间,将种群调整为合理区间
pos = num - 1#种群数量慢慢递减,pos是现在的种群数量
if flag_limit_area(animals[pos], limit_area):#判断是否在要求区间
num -= 1
else:
animals[pos] = np.random.randint(2, size=(1, DNA_bit * DNA_num))#重新生成新解
########################将符合要求的200种群按代数迭代,开始选择、交叉、变异######################################################
#生物遗传进化###################优化算法的部分#################################
# 模拟进化选择generator_n轮
for i in range(generator_n):#按着代数进行循环
fitness_score = get_fitness(animals) # 计算适应度,返回每一个种群被选择的概率,一个种群是一个解,输入200个种群
#返回的是200长度的数组,往下输入进行【选择操作】
selected_animals = select_animal(animals, fitness_score) # 根据适应度值,适者生存,选了200个
#根据概率(适应度分数)选择了200个种群,往下进行交叉变异
animals = crossover_and_variation(selected_animals, cross_rate) # 生殖、变异,根据设置的代数重复计算,输入200个种群
###############完成选择、交叉、变异的操作###############################################################
#将选择、交叉、变异后的种群放入结果,此时已产生最优结果
get_result(animals)
基本上每一行代码都写了注释,慢慢看绝对容易理解。