为了对付课设,启发式算法是一个划水的好帮手,特别是用启发式算法求解TSP问题,本文将利用遗传算法求解,听到是遗传算法觉得不咋地?别急,当你的改进足够多的时候,遗传算法都能玩出花来。
大家做课设,肯定是需要ppt的,为了便于大家划水,我将ppt的关键部分截图,大家各取所需。
研究背景
关键技术
实践创新
重点!!!!!!!!
效果图
1原始遗传算法
2 改进1
3 改进2
4 改进3
5 改进4
6 改进5
7.蚁群算法
迭代对比
源码!!!!
#遗传算法求解TSP问题完整代码:
import copy
import time
from math import exp
import numpy as np
import matplotlib.pyplot as plt
import random
# 处理数据
coord = []
with open("data.txt", "r") as lines:
lines = lines.readlines()
for line in lines:
xy = line.split()
coord.append(xy)
coord = np.array(coord)
w, h = coord.shape
coordinates = np.zeros((w, h), float)
for i in range(w):
for j in range(h):
coordinates[i, j] = float(coord[i, j])
# print(coordinates)
# 得到距离矩阵
distance = np.zeros((w, w))
for i in range(w):
for j in range(w):
distance[i, j] = distance[j, i] = np.linalg.norm(coordinates[i] - coordinates[j])
# 种群数
count = 300
# 进化次数
iter_time = 2000
# 最优选择概率
retain_rate = 0.3 # 适应度前30%可以活下来
# 弱者生存概率
random_select_rate = 0.5
# 变异
mutation_rate = 0.1
# 改良
gailiang_N = 2000
##按批选择
batch_size = 30 #################################################可以进行控制或者选择,最终选择最优的情况
# 适应度
CREATIVE = 0.1 ###在交叉或者遗传部分可能会利用信息素进行子个体的生成
CREATIVE_BAD = 0.5 ####有一定的概率会生成出适应度最差的个体
def variation_tools(list,point_1,point_2):
##确保point_1 < point_2
if point_1 > point_2:
cur = point_1
point_1 = point_2
point_2 = cur
i = 0
while(True):
# print(point_1+i)
cur = list[point_1+i]
list[point_1+i] = list[point_2-i]
list[point_2-i] = cur
i+=1
if (point_1+i) - (point_2-i) > 0:
break
return list
def get_total_distance(x): ##获取x(一个可行解列表)
dista = 0
for i in range(len(x)):
if i == len(x) - 1:
dista += distance[x[i]][x[0]]
else:
dista += distance[x[i]][x[i + 1]]
return dista
# 初始种群的改良
########################################进行优化,不仅仅是对于两个点进行交换,也可能对两个点之间的序列进行倒序排列,并且会有一定的概率接受次优解(效果差于之前的个体)
G1 = 0.1
def gailiang(x):
distance = get_total_distance(x) ##利用distance记录当前的解(x)的总距离
gailiang_num = 0 #改良的数量
while gailiang_num < gailiang_N:
while True:
a = random.randint(0, len(x) - 1)
b = random.randint(0, len(x) - 1)
if a != b:
break
if random.random() < G1:
new_x = x.copy()
new_x = variation_tools(new_x,a,b)
else:
new_x = x.copy()
temp_a = new_x[a]
new_x[a] = new_x[b]
new_x[b] = temp_a
if get_total_distance(new_x) < distance:
x = new_x.copy()
######以一定的概率接受次优解
elif random.random()<=(exp((get_total_distance(new_x)-distance) / gailiang_num)-1):
x = new_x.copy()
gailiang_num += 1
return x
# 自然选择
def nature_select(population):
grad = [[x, get_total_distance(x)] for x in population]
grad = [x[0] for x in sorted(grad, key=lambda x: x[1])]
# 强者
retain_length = int(retain_rate * len(grad))
parents = grad[: retain_length]
# 生存下来的弱者
for ruozhe in grad[retain_length:]:
if random.random() < random_select_rate:
parents.append(ruozhe)
return parents
# 交叉繁殖
def crossover(parents):
target_count = count - len(parents)
children = []
while len(children) < target_count:
while True:
male_index = random.randint(0, len(parents)-1)
female_index = random.randint(0, len(parents)-1)
if male_index != female_index:
break
male = parents[male_index]
female = parents[female_index]
left = random.randint(0, len(male) - 2)
right = random.randint(left, len(male) - 1)
gen_male = male[left:right]
gen_female = female[left:right]
child_a = []
child_b = []
len_ca = 0
for g in male:
if len_ca == left:
child_a.extend(gen_female)
len_ca += len(gen_female)
if g not in gen_female:
child_a.append(g)
len_ca += 1
len_cb = 0
for g in female:
if len_cb == left:
child_b.extend(gen_male)
len_cb += len(gen_male)
if g not in gen_male:
child_b.append(g)
len_cb += 1
children.append(child_a)
children.append(child_b)
return children
# 变异操作
def mutation(children):
for i in range(len(children)):
if random.random() < mutation_rate:
while True:
u = random.randint(0, len(children[i]) - 1)
v = random.randint(0, len(children[i]) - 1)
if u != v:
break
if random.random() < G1:
children[i] = variation_tools(children[i],u,v)
else:
temp_a = children[i][u]
children[i][u] = children[i][v]
children[i][v] = temp_a
return children
def get_result(population):
grad = [[x, get_total_distance(x)] for x in population]
grad = sorted(grad, key=lambda x: x[1])
return grad[0][0], grad[0][1]
###轮盘赌法
def random_way(P_IJ): # 传入了转移概率,利用轮盘法进行选择 返回值为索引i----->选择的转移概率中的第n个点
ran = random.random()
# length = len(P_IJ[:, 0])
length = len(P_IJ)
t = [0]
sum = 0
for tem in range(0,length):
sum += P_IJ[tem]
# print(sum)
t.append(sum)
# print(t)
for i in range(0,len(t)-1):
# print(t[i])
# print(ran)
if t[i]<=ran and t[i+1] > ran:
return i
def find_max(list):
##对于信息素进行归一化
list1 = copy.deepcopy(list)
s = sum(list1)
for i in range(0,len(list1)):
list1[i] = list1[i]/s
##归一化处理完毕
return random_way(list1) ###返回最终结果---是一个索引(从0开始)
def find_min(list):
list1 = copy.deepcopy(list)
for i in range(0, len(list1)):
if list1[i] != 0:
list1[i] = 1/list1[i]
else:
list1[i] = 0.1
s = sum(list1)
for i in range(0,len(list1)):
list1[i] = list1[i]/s
return random_way(list1)
################################可以根据信息素浓度产生下一个解
def generate_init(pheromone):
result = []
result.append(random.randint(0,w-1))##确定了第一个点
for i in range(1,w):##用来确定后续的点
point_now = result[i-1]##确定当前点
while(True):###确保找出来的点不在之前的路径中
next_point = find_max(pheromone[point_now,:])
if next_point not in result:
break
result.append(next_point)
return result
def generate_init_bad(pheromone): ###生成最差的个体
result = []
result.append(random.randint(0,w-1))##确定了第一个点
for i in range(1,w):##用来确定后续的点
point_now = result[i-1]##确定当前点
while(True):###确保找出来的点不在之前的路径中
next_point = find_min(pheromone[point_now,:])
if next_point not in result:
break
result.append(next_point)
return result
def new_pheromone(list,pheromone):
pheromone = pheromone * (1 - 0.001)
for i in list:
fit = 1/get_total_distance(i)
for kk in range(0,len(i)-1):
pheromone[i[kk],i[kk+1]] += fit
pheromone[i[kk+1], i[kk]] = pheromone[i[kk],i[kk+1]]
return pheromone
def chuangxin(p1,parents):
# print(parents)
c1 = generate_init(p1)
c2 = generate_init(p1)
c3 = generate_init(p1)
c4 = generate_init(p1)
c5 = generate_init(p1)
# grad = [[x, get_total_distance(x)] for x in population]
# grad = [c1,get_total_distance(c1)]
parents.append(c1)
parents.append(c2)
parents.append(c3)
parents.append(c4)
parents.append(c5)
if random.random()<CREATIVE_BAD:
c6 = generate_init_bad(p1)
c7 = generate_init_bad(p1)
c8 = generate_init_bad(p1)
c9 = generate_init_bad(p1)
c10 = generate_init_bad(p1)
# grad = [[x, get_total_distance(x)] for x in population]
# grad = [c1, get_total_distance(c2)]
parents.append(c6)
parents.append(c7)
parents.append(c8)
parents.append(c9)
parents.append(c10)
return parents
def run():
pheromone = np.ones([w,w]) ##信息素浓度矩阵
t1 = time.time()
population = []
# 初始化种群 + 改良阶段###############################
index = [i for i in range(w)] ###创建一个最简单的矩阵,为后面随机生成路径提供了基础
t2 = time.time()
G2 = 0.2###利用信息素产生解的概率
for i in range(count): ##count
if i<batch_size or random.random()<G2:
x = index.copy()
random.shuffle(x)
x = gailiang(x)###改进
# gailiang(x) ###改进
population.append(x)
else:####根据信息素浓度产生
x = generate_init(pheromone)
###可以对x再进行一些处理,暂时先空着
population.append(x)
if i%batch_size == 0:
cur_p = population[i-20:i] ##进行切片操作,用来进行信息素的更新c ###重点
###进行信息素的更新
pheromone = new_pheromone(cur_p,pheromone)
t3 = time.time()
print("改良所花费的时间为:",t3-t2)
#######################################################################
distance_list = []
result_cur_best, dist_cur_best = get_result(population) #第一个为序列,第二个为距离
distance_list.append(dist_cur_best)
i = 0
while i < iter_time:
# 自然选择
parents = nature_select(population)
####有一定的概率会利用信息素进行个体的生成 , 有可能会生成最好的个体,也可能会生成最差的个体(逃离死区)
##对于生成出的个体,直接加入到parents中即可
if random.random() < CREATIVE:
parents = chuangxin(pheromone,parents)##注意返回值
# 繁殖
children = crossover(parents)
# 变异
mutation(children)
# 更新
population = parents + children
result_cur_best, dist_cur_best = get_result(population)
distance_list.append(dist_cur_best)
i = i + 1
if i % 50 == 0: ###对信息素进行更新
# print("更新一次")
pheromone = new_pheromone(population, pheromone)
# print(result_cur_best)
# print(dist_cur_best)
for i in range(len(result_cur_best)):
result_cur_best[i] += 1
result_path = result_cur_best
result_path.append(result_path[0])
return dist_cur_best
if __name__ == '__main__':
pheromone = np.ones([w, w]) ##信息素浓度矩阵
t1 = time.time()
population = []
# 初始化种群 + 改良阶段###############################
index = [i for i in range(w)] ###创建一个最简单的矩阵,为后面随机生成路径提供了基础
t2 = time.time()
G2 = 0.2 ###利用信息素产生解的概率
for i in range(count): ##count
if i < batch_size or random.random() < G2:
x = index.copy()
random.shuffle(x)
x = gailiang(x) ###改进
# gailiang(x) ###改进
population.append(x)
else: ####根据信息素浓度产生
x = generate_init(pheromone)
###可以对x再进行一些处理,暂时先空着
population.append(x)
if i % batch_size == 0:
cur_p = population[i - 20:i] ##进行切片操作,用来进行信息素的更新c ###重点
###进行信息素的更新
pheromone = new_pheromone(cur_p, pheromone)
t3 = time.time()
print("改良所花费的时间为:", t3 - t2)
#######################################################################
distance_list = []
result_cur_best, dist_cur_best = get_result(population) # 第一个为序列,第二个为距离
distance_list.append(dist_cur_best)
i = 0
while i < iter_time:
# 自然选择
parents = nature_select(population)
####有一定的概率会利用信息素进行个体的生成 , 有可能会生成最好的个体,也可能会生成最差的个体(逃离死区)
##对于生成出的个体,直接加入到parents中即可
if random.random() < CREATIVE:
parents = chuangxin(pheromone, parents) ##注意返回值
# 繁殖
children = crossover(parents)
# 变异
children = mutation(children)
# 更新
population = parents + children
result_cur_best, dist_cur_best = get_result(population)
distance_list.append(dist_cur_best)
i = i + 1
if i % 50 == 0: ###对信息素进行更新
# print("更新一次")
pheromone = new_pheromone(population, pheromone)
# print(result_cur_best)
# print(dist_cur_best)
for i in range(len(result_cur_best)):
result_cur_best[i] += 1
result_path = result_cur_best
result_path.append(result_path[0])
t4 = time.time()
print()
print("最佳路径为:", result_path)
print("最短的长度为:", dist_cur_best)
print("花费时间:",t4-t1)
# 画图
X = []
Y = []
for index in result_path:
X.append(coordinates[index - 1, 0])
Y.append(coordinates[index - 1, 1])
plt.rcParams['font.sans-serif'] = 'SimHei' # 设置中文显示
plt.rcParams['axes.unicode_minus'] = False
plt.figure("改进算法轨迹图")
plt.plot(X, Y, '-o')
for i in range(len(X)):
plt.text(X[i] + 0.05, Y[i] + 0.05, str(result_path[i]), color='red')
plt.xlabel('横坐标')
plt.ylabel('纵坐标')
plt.title('轨迹图')
plt.savefig("路径图.png")
plt.figure("改进算法迭代图")
plt.plot(np.array(distance_list))
plt.title('优化过程')
plt.ylabel('最优值')
plt.xlabel('代数({}->{})'.format(0, iter_time))
plt.savefig("迭代过程图.png")
plt.show()
直接就可以运行了,算法在当前目录下还需要data.txt文件,其中的数据为:
1.3 2.3
3.6 1.35
4.17 2.24
3.7 1.4
3.4 1.5
3.3 1.56
3.2 1.2
4.2 1.08
4.3 0.8
4.4 0.6
3.0 2.0
2.56 1.75
2.78 1.49
2.38 1.67
1.332 0.695
3.715 1.678
3.918 2.179
4.061 2.37
3.78 2.212
3.676 2.578
4.029 2.838
4.263 2.931
3.429 1.908
3.507 2.376
3.394 2.643
2.33 2.55
0.12 2.67
1.75 2.35
3.23 0.66
3.46 2.75
2.44 0.78
2.34 1.75
0.53 0.6
0.78 1.41
1.39 1.45
1.49 2.10
1.03 2.99
0.59 2.21
0.12 2.32
0.162 0.667
0.248 1.179
1.2 1.87
1.48 2.85
3.7 0.98
1.82 0.97
1.73 2.97
4.2 1.7
3.82 0.87
1.918 0.667
1.074 1.179
1.738 1.364
2.118 2
0.38 2.95
0.305 0.777
1.928 1.969
4.5 1.428
1.804 2.65
3.4 2.1
2.7 2.28
0.979 2.065
可自行增加和删除,每一行代表了一个点的横纵坐标,中间以空格作为分割。
致歉!
由于本人的疏忽,代码比较乱,很多改进的算法不知道放哪去了,所以只给出了最后的改进算法,如果你是为了课设,可以直接说出一个改进(最后这个),然后把本文的步骤随便删掉几个作为原始的遗传算法进行比较即可。