1. 算法简介
利用遗传算法将N个点均匀散布到一个正方形区域内,尽可能充分均匀地覆盖整个平面区域,包括边界。其中N≥5,测试N=5,6,8,9,12,15,16,18,20的情况。
注:独立编码,未使用遗传算法工具箱
2. 运行环境
编码语言:Python
集成开发环境:PyCharm Community Edition 2020.3.3 x64
解释器:Anaconda-Python 3.8
处理器:Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
机带RAM:16.0 GB
3. 使用说明
1、直接运行全部代码,按照提示"请输入点的数量:"输入点数N,待程序运行后即可得到优化后的结果及可视化散点图。
2、以下五个参数均可调整:
参数 | 含义 | 取值范围 |
POP_SIZE | 种群数量 | 参考遗传代数调整大小,一般取10-1000 |
CROSSOVER_RATE | 交叉率 | 一般取0.8 |
MUTATION_RATE | 变异率 | 一般取0.1 |
GENERATIONS | 遗传代数 | 参考种群数量调整大小,一般取500-50000 |
b | 变异程度因子 | (0,1),取值越大,遗传后期变异程度越小 |
3、相同点数N时,适应度best_fi tness越大,说明结果越接近最优解。
4. 结构说明
(1)解的表示
采用[0,1000]内的正整数表示每一个坐标点的数值,故通过等比例放缩,可以得到单位正方 形平面内小数点后3位的精度。
利用numpy库中的random函数生成所需的种群,如下
population = np.random.randint(0, 1000, size=(POP_SIZE, N*2))
例如,当
N = 6
POP_SIZE = 5
随机生成的种群为
[[273, 557, 707, 365, 231, 392, 857, 937, 165, 996, 85, 797],
[54, 188, 291, 146, 822, 108, 221, 419, 334, 218, 475, 714],
[673, 622, 194, 451, 904, 170, 977, 208, 918, 960, 803, 52],
[803, 893, 978, 266, 460, 762, 384, 449, 620, 831, 991, 936],
[8, 518, 248, 807, 118, 592, 201, 538, 537, 516, 838, 570]]
其中每行表示一个个体,每个个体有2*N的坐标点,N个坐标对,第[2i]个坐标点和第[2i+1]个坐标点为一组坐标对,i∈[0,N]。
(2)交叉、变异算子
交叉算子Crossover_and_Mutation有三个参数,分别为当前种群、交叉率、当前遗传代数
def Crossover_and_Mutation(pop, CROSSOVER_RATE, gen):
for i in range(len(pop)):#遍历种群中的每一个个体,将该个体作为父体
选择一个父体
if np.random.rand() < CROSSOVER_RATE:#以CROSSOVER_RATE的概率交叉
种群中选择另一个个体,并将该个体作为母体
随机产生交叉的系数
执行交叉计算
population.append(Mutation(子代, MUTATION_RATE, gen))
#交叉算子内嵌变异
变异算子Crossover_and_Mutation有三个参数,分别为子代、变异率、当前遗传代数
def Mutation(子代, MUTATION_RATE, gen):#非均匀变异
if np.random.rand() < MUTATION_RATE:#以MUTATION_RATE的概率变异
随机产生变异的系数
执行变异计算
对变异结果进行约束条件限制
return 变异后的个体
变异采用了实数编码的非均匀变异
其中:τ取值-1或1, 且取每个值的概率为50%;
r是[0, 1]之间的随机数;
tmax是最大遗传代数;
t是当前遗传代数;
b是一个可调参数。
(3)适应度函数
适应度函数get_fi tness有两个参数,分别为个体、当前遗传代数
def cal_fitness(ind, gen):#个体适应度计算函数
for i in range(N):
for j in range(N-1, i, -1):
计算两点间的距离fit保留最小距离
return 最小距离
def get_fitness(pop, gen):#适应度计算函数
for i in range(len(pop)):
计算该个体适应度
将该个体适应度及DNA存入字典
将该个体适应度存入fitness列表
return fitness列表
(4)选择算子
适应度函数select有一个参数,为fi tness列表
def select(fit):#选择函数
for i in range(len(fit_temp)):
适应度求和
for i in range(len(fit_temp)):
得到单个个体被选择概率
#轮盘赌
#按概率选择
#生成选择后的种群
#锦标赛
fitness列表降序排序
取前POP_SIZE个个体
生成选择后的种群
return 选择后的种群
(5)迭代运行
start = time.time()#程序开始计时
for i in range(GENERATIONS):#种群迭代进化GENERATIONS代
Crossover_and_Mutation(population, CROSSOVER_RATE, i)
#种群通过交叉变异产生后代
fitness = get_fitness(population, i)[:]
#对种群中的每个个体进行评估
population = select(fitness)[:]
#选择生成新的种群
输出进度
获取最大适应度个体,并保存
(6)结果输出及可视化
print("best_fitness:",best_fitness)#输出最佳适应度
for i in range(N):#绘制结果散点图
plt.scatter(x[2*i], x[2*i+1])
end = time.time()#程序结束计时
输出参数POP_SIZE,CROSSOVER_RATE,MUTATION_RATE,GENERATIONS
输出程序运行时间
5. 常见问题说明
1、源代码提供了实数编码和二进制编码两个版本,参考实际运行结果,实数编码的运行速度赶快,优化结果更加,故此代码说明均以实数编码的程序为例展开。
2、由于平方数个点的最优状态的收敛域较窄,使得9和16个点较难以很高的概率收敛到最优;同样由于算法的随机性,复现运行结果时会出现适应度大于或小于附录中运行结果的情况。
6. 声明
源代码链接 https://download.csdn.net/download/weixin_48365310/87587721
全部程序均独立编码,算法框架参考自博客:
遗传算法详解 附python代码实现 https://blog.csdn.net/ha_ha_ha233/article/details/91364937
仅供学习交流,代码风格较差,望勿借鉴
附:运行结果
N=5
best_fi tness: 707.108 POP_SIZE = 20 CROSSOVER_RATE = 0.8 MUTATION_RATE = 0.1 GENERATIONS = 10000 Running time: 29.826 seconds |
N=6
best_fi tness: 592.444 POP_SIZE = 500 CROSSOVER_RATE = 0.8 MUTATION_RATE = 0.1 GENERATIONS = 500 Running time: 152.439 seconds |
N=8
best_fi tness: 501.092 POP_SIZE = 20 CROSSOVER_RATE = 0.8 MUTATION_RATE = 0.1 GENERATIONS = 10000 Running time: 64.601 seconds |
N=9
best_fi tness: 477.010 POP_SIZE = 500 CROSSOVER_RATE = 0.8 MUTATION_RATE = 0.1 GENERATIONS = 500 Running time: 115.770 seconds |
N=12
best_fi tness: 349.997 POP_SIZE = 10 CROSSOVER_RATE = 0.8 MUTATION_RATE = 0.1 GENERATIONS = 20000 Running time: 132.948 seconds |
N=15
best_fi tness: 295.223 POP_SIZE = 10 CROSSOVER_RATE = 0.8 MUTATION_RATE = 0.1 GENERATIONS = 20000 Running time: 195.829 seconds |
N=16
best_fi tness: 291.001 POP_SIZE = 20 CROSSOVER_RATE = 0.8 MUTATION_RATE = 0.1 GENERATIONS = 30000 Running time: 654.283 seconds |
N=18
best_fi tness: 257.236 POP_SIZE = 20 CROSSOVER_RATE = 0.8 MUTATION_RATE = 0.1 GENERATIONS = 20000 Running time: 565.277 seconds |
N=20
best_fi tness: 247.537 POP_SIZE = 10 CROSSOVER_RATE = 0.8 MUTATION_RATE = 0.1 GENERATIONS = 10000 Running time: 202.108 seconds |
源代码
import numpy as np
import matplotlib.pyplot as plt
import copy
import time
POP_SIZE = 20 #种群数量
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.1
GENERATIONS = 10000
b = 0.7 #变异程度因子
N = int(input("请输入点的数量:"))
population = np.random.randint(0, 1000, size=(1000, N*2))
population = population.tolist()
fitness = [0 for i in range(POP_SIZE)] #初始化fitness列表
dic = {} #初始化字典
def Crossover_and_Mutation(pop, CROSSOVER_RATE, gen):
for i in range(len(pop)): # 遍历种群中的每一个个体,将该个体作为父体
father = pop[i]
if np.random.rand() < CROSSOVER_RATE: # 以CROSSOVER_RATE的概率交叉
ch1 = np.zeros(2 * N, dtype='i')
ch2 = np.zeros(2 * N, dtype='i')
mother = pop[np.random.randint(POP_SIZE)] # 在种群中选择另一个个体,并将该个体作为母体
cross_position = np.random.randint(0, N * 2) # 随机产生交叉的点
for j in range(2 * N - cross_position):
alpha = np.random.rand()
ch1[cross_position+j] = alpha * mother[cross_position+j] + (1 - alpha) * father[cross_position+j]
# 孩子得到位于交叉点后的母亲的基因
if ch1[j] > 1000:
ch1[j] = 1000
elif ch1[j] < 0:
ch1[j] = 0
ch2[j] = alpha * father[j] + (1 - alpha) * mother[j]
# 孩子得到位于交叉点后的母亲的基因
if ch2[j] > 1000:
ch2[j] = 1000
elif ch2[j] < 0:
ch2[j] = 0
population.append(Mutation(ch1, MUTATION_RATE, gen))#变异
population.append(Mutation(ch2, MUTATION_RATE, gen))#变异
def Mutation(seg, MUTATION_RATE, gen):#非均匀变异策略
if np.random.rand() < MUTATION_RATE: # 以MUTATION_RATE的概率变异
mutate_position = np.random.randint(2 * N) # 随机产生一个实数,代表要变异基因的位置
tau = np.random.choice([-1, 1], size=1, p=[0.5, 0.5])
seg[mutate_position] = seg[mutate_position] + tau[0] * 1000 * (
1 - pow(np.random.rand(), pow((1 - gen / GENERATIONS), b)))
if seg[mutate_position] > 1000: #约束最大值
seg[mutate_position] = 1000
elif seg[mutate_position] < 0: #约束最小值
seg[mutate_position] = 0
return seg
def cal_fitness(ind, gen): # 个体适应度计算函数
fit = 0
minn = 10000 # 任意大数
'''if gen <= N*500: #gen % int((N-1)/2) == 0:
for i in range(N):
for j in range(N - 1, i, -1):
fit += 1 / (np.sqrt(pow(ind[2 * i] - ind[2 * j], 2) + pow(ind[2 * i + 1] - ind[2 * j + 1], 2)) + 1e-3) # 能量计算
return 1 / fit
else:'''
for i in range(N):#N个点之间最小距离作为适应度
for j in range(N - 1, i, -1):
fit = np.sqrt(
pow(ind[2 * i] - ind[2 * j], 2) + pow(ind[2 * i + 1] - ind[2 * j + 1], 2)) + 1e-3 # 最大的最小值
if fit < minn:
minn = fit
return minn
def get_fitness(pop, gen): # 适应度计算函数
fitness_value = 0
fit = []
for i in range(len(pop)):
fitness_value = cal_fitness(pop[i], gen) # 计算该个体适应度
dic[fitness_value] = pop[i] # 将该个体适应度及DNA存入字典
fit.append(fitness_value) # 将该个体适应度存入fitness列表
return fit
def select(fit): # 选择函数
sum = 0
fit_temp = fit[:]
for i in range(len(fit_temp)): # 适应度求和
sum += fit_temp[i]
for i in range(len(fit_temp)): # 选择概率
fit_temp[i] = fit_temp[i] / sum
# 轮盘赌
#pop_temp = np.random.choice(fit, size = POP_SIZE, replace = True, p = fit_temp)#按概率选择
#pop_temp.tolist()
#for i in range(POP_SIZE): #生成选择后的种群
# pop_temp[i] = copy.deepcopy(dic[pop_temp[i]])
# 锦标赛
fit.sort(reverse=True)
pop_temp = population[0:POP_SIZE]
#pop_temp.tolist()
for i in range(POP_SIZE): # 生成选择后的种群
pop_temp[i] = dic[fit[i]]
return pop_temp
start = time.time()#开始计时
best_fitness = 0
for i in range(GENERATIONS): # 种群迭代进化GENERATIONS代
Crossover_and_Mutation(population, CROSSOVER_RATE, i) # 种群通过交叉变异产生后代
fitness = get_fitness(population, i)[:] # 对种群中的每个个体进行评估
population = select(fitness)[:] # 选择生成新的种群
print("进度:%d/%d current_fitness = " % (i, GENERATIONS), max(fitness))
if max(fitness) > best_fitness:
best_fitness = max(fitness)
best_one = dic[best_fitness]
dic = {}
dic[best_fitness] = best_one
x = dic[best_fitness]
print(x)
print("best_fitness:",best_fitness)
for i in range(N):#绘制散点图
plt.scatter(x[2*i], x[2*i+1])
end = time.time()#结束计时
print('POP_SIZE = %s\nCROSSOVER_RATE = %s\nMUTATION_RATE = %s\nGENERATIONS = %s'
%(POP_SIZE,CROSSOVER_RATE,MUTATION_RATE,GENERATIONS))
print('Running time: %s seconds'%(end-start))
plt.show()