一.实验目的
本实验课程是计算机、智能、物联网等专业学生的一门专业课程,通过实验,帮助学生更好地掌握人工智能相关概念、技术、原理、应用等;通过实验提高学生编写实验报告、总结实验结果的能力;使学生对智能程序、智能算法等有比较深入的认识。要掌握的知识点如下:
掌握人工智能中涉及的相关概念、算法;
熟悉人工智能中的知识表示方法;
掌握问题表示、求解及编程实现;
熟悉和掌握遗传算法的基本概念和基本思想;
理解和掌握遗传算法的各个操作算子,能够用选定的编程语言设计简单的遗传优化系统;
通过实验培养学生利用遗传算法进行问题求解的基本技能。
二.实验内容:
以N个节点的TSP(旅行商问题)问题为例,应用遗传算法进行求解,求出问题的最优解。
1 旅行商问题
旅行商问题(Traveling Salesman Problem, TSP),又译为旅行推销员问题、货担郎问题,简称为TSP问题,是最基本的路线问题。假设有n个可直达的城市,一销售商从其中的某一城市出发,不重复地走完其余n-1个城市并回到原出发点,在所有可能的路径中求出路径长度最短的一条。
TSP问题是组合数学中一个古老而又困难的问题,也是一个典型的组合优化问题,现已归入NP完备问题类。NP问题用穷举法不能在有效时间内求解,所以只能使用启发式搜索。遗传算法是求解此类问题比较实用、有效的方法之一。
下面给出30个城市的位置信息:
表1 Oliver TSP问题的30个城市位置坐标
城市编号 |
坐标 |
城市编号 |
坐标 |
城市编号 |
坐标 |
1 |
(87,7) |
11 |
(58,69) |
21 |
(4,50) |
2 |
(91,38) |
12 |
(54,62) |
22 |
(13,40) |
3 |
(83,46) |
13 |
(51,67) |
23 |
(18,40) |
4 |
(71,44) |
14 |
(37,84) |
24 |
(24,42) |
5 |
(64,60) |
15 |
(41,94) |
25 |
(25,38) |
6 |
(68,58) |
16 |
(2,99) |
26 |
(41,26) |
7 |
(83,69) |
17 |
(7,64) |
27 |
(45,21) |
8 |
(87,76) |
18 |
(22,60) |
28 |
(44,35) |
9 |
(74,78) |
19 |
(25,62) |
29 |
(58,35) |
10 |
(71,71) |
20 |
(18,54) |
30 |
(62,32) |
最优路径为:1 2 3 4 6 5 7 89 10 11 12 13 14 15 16 17 19 18 20 21 22 23 24 25 28 26 27 29 30
其路径长度为:424.869292
也可取前10个城市的坐标进行测试:
表2 Oliver TSP问题的10个城市位置坐标
城市编号 |
坐标 |
1 |
(87,7) |
2 |
(91,38) |
3 |
(83,46) |
4 |
(71,44) |
5 |
(64,60) |
6 |
(68,58) |
7 |
(83,69) |
8 |
(87,76) |
9 |
(74,78) |
10 |
(71,71) |
有人求得的最优路径为: 0 3 5 4 9 8 7 6 2 1 0
路径长度是166.541336
上述10个城市的求解中编号从0开始,把所有路径搜索完又返回到出发节点。
2 问题描述
应用遗传算法求解30/10个节点的TSP(旅行商问题)问题,求问题的最优解。
三.算法思路:
利用遗传算法解决tsp问题。
遗传算法:求十个点之间最短路径(包括最后回到起点)
yichuan(indata:list,data:list):
1.得到十个点
2.10个点随机排序变成8个排序组合
3.分别4次抽一个出来基因突变,4次抽两个出来对其中一个基因交换
4.两个八个进行按照大小适合度的排序,若新的对应那个比旧的对应的小,则交换。
5.重复直到连续n次最小适合度值不变,输出最小适合度对应路径。
基因突变:
1.选择出一个路径的排列组合
2.随机选择其中连续的k个路径点进行改变顺序
基因交换:
对其中一个排列组合选择其中k个连续点。
把这连续的k个点在另一个排列组合中删掉。
在这k个连续的点 连续地 加入到被删掉这几个点的排列组合中。
四.代码:
读取每个点坐标:
data.txt:坐标可以更多维度,data.txt里面设置坐标即可。
目前为:点编号,x,y
1,87,7
2,91,38
3,83,46
4,71,44
5,64,60
6,68,58
7,83,69
8,87,76
9,74,78
10,71,71
11,58,69
12,54,62
13,51,67
14,37,84
15,41,94
16,2,99
17,7,64
18,22,60
19,25,62
20,18,54
21,4,50
22,13,40
23,18,40
24,24,42
25,25,38
26,41,26
27,45,21
28,44,35
29,58,35
30,62,32
main.py:
import random
import copy
def openreadtxt(file_name):
data = []
file = open(file_name, 'r', encoding='utf-8') # 打开文件
file_data = file.readlines() # 读取所有行
for row in file_data:
tmp_list = row.split(',') # 按‘,'切分每行的数据
tmp_list[-1] = tmp_list[-1].replace('\n', '') # 去掉换行符
data.append(tmp_list) # 将每行数据插入data中
return data
def indata(data):
indata = []
print('请输入10个城市编号,从1-',data[-1][0],',计算机将求出最小距离:')
for i in range(0, 10):
print('请输入第', (i + 1), '个编号:')
theindata = int(input())
while 1:
if theindata not in range(1, 31) or theindata in indata:
print('输入数据范围错误或者已存在于data中,请重新输入:')
theindata = int(input())
else:
break
indata.append(theindata)
return indata
# def kernalfind(data_xiabiao, data: list):#return可能有问题
# for idata in range(len(data)):
# if data[idata][0] == data_xiabiao:
# return idata
def sorttsp(datas: list, data: list):
data_value = []
begin_data = 0
thelen = len(data[0])
#添加头部点,以计算结尾回去的距离
for datas_data in datas:
datas_data.append(datas_data[0])
for thedatas in datas: # 从8列数据里面拿出1列数据
thevalue = 0 # 计算10+1个点的距离
for thedata in thedatas: # 计算10+1个点的距离之和
iivalue = 0 # 计算两点间距离
if thedata == thedatas[0]:
begin_data = thedata
else:
#begin_data = kernalfind(begin_data, data)
#thedata_1 = kernalfind(thedata, data)
begin_data=int(begin_data)-1
thedata_1 = int(thedata) - 1
for ii in range(1, thelen):
iivalue = iivalue + (int(data[begin_data][ii]) - int(data[thedata_1][ii])) ** 2
thevalue = thevalue + iivalue ** (1 / 2)
begin_data = thedata
data_value.append(thevalue)
#去除最后的节点,恢复原状
for datas_data in datas:
datas_data.pop()
return data_value
def value_data(datas_v: list, datas: list, ):
for data in range(len(datas)):
datas[data].append(datas_v[data])
return datas
'''
yichuan(indata:list,data:list):
1.得到十个点
2.10个点随机排序变成8个排序组合
3.分别4次抽一个出来基因突变,4次抽两个出来对其中一个基因交换
4.两个八个进行按照大小适合度的排序,若新的对应那个比旧的对应的小,则交换。
5.重复直到连续10次最小适合度值不变,输出最小适合度对应路径。
'''
def yichuan(indata: list, data: list):
all_value=[]#存放所有value
all_road=[]#存放所有路径
i = 0 # 连续20次最小适合度值不变结束
i_i = 0 # 计算最小适合度下标
i_i_value=0#存放最终最小value
datas_1 = [] # 存放旧随机8个排序组合
for ii in range(0, 8): # 得到8个排序组合
indata = copy.deepcopy(indata)#防止改变datas_1里面的元素
random.shuffle(indata)
while 1:
if indata in datas_1:
indata = copy.deepcopy(indata)#值和id都一样
random.shuffle(indata)
else:
datas_1.append(indata)
break
while i < 20:#可以改变,然后就可以让更多次最小适合度不变才结束。
print('开始一次,i=',i)
datas_2 = [] # 存放新随机8个排序组合
# for ii in range(0, 8): # 得到8个排序组合
# random.shuffle(indata)
# indata = copy.deepcopy(indata)
# while 1:
# if indata in datas_1:
# indata = copy.deepcopy(data)
# else:
# datas_1.append(indata)
# break
# --------------------基因突变-----------------------------------
# 4次抽一个出来基因突变
xs = []#存放要进行基因突变的4个排列组合
for iii in range(0, 4): # 找到8个里面要抽出来的4个
# random.randint(0, 7)#(1,10)==[1,10]而不是[1,10)
while 1: # 要4个不重复的数字
x1 = random.randint(0, 7)
if x1 not in xs:
xs.append(x1)
break
# 对四个数据进行基因突变
for i1 in xs: # xs里面是4个下标,找到每个下标
x2 = random.randint(0, 7) # 找到下标,进行3个元素的随机变化
new_data = copy.deepcopy(datas_1[i1])
x3 = []#存放要基因突变的3个元素
x3.append(datas_1[i1][x2])
x3.append(datas_1[i1][x2 + 1])
x3.append(datas_1[i1][x2 + 2])
random.shuffle(x3)
new_data[x2] = x3[0]
new_data[x2 + 1] = x3[1]
new_data[x2 + 2] = x3[2]
# new_data为这行数据突变完的结果
datas_2.append(new_data)
# --------------------基因交换-----------------------------------
# 再对其中四个数据进行基因交换
xs = []
for iii in range(0, 4): # 找到要抽出来的四个
# random.randint(0, 7)#(1,10)==[1,10]而不是[1,10)
while 1: # 要4个不重复的数字
x1 = random.randint(0, 7)
if x1 not in xs:
xs.append(x1)
break
for i1 in xs: # xs里面是4个下标,找到每个下标
x1 = 0
#new_data=copy.deepcopy(new_data)
new_data = copy.deepcopy(datas_1[i1])
while 1: # 要1个不与重复i的数字
x1 = random.randint(0, 7)#8列里面选出一个不等于i的那一列数据
if x1 != i1:
break
# i和x1是两个数据的下标,对这两个数据进行基因交换
x2 = random.randint(0, 7)#10个数据里面随机选出3个连续的数据的起点
ii = x2
while ii != x2 + 3:
new_data.remove(datas_1[x1][ii])
ii = ii + 1
new_data.append(datas_1[x1][x2])
new_data.append(datas_1[x1][x2 + 1])
new_data.append(datas_1[x1][x2 + 2])
datas_2.append(new_data)
# 现在有八个新数据了
# -------------两个八个进行按照大小适合度的排序,若新的对应那个比旧的对应的小,则交换。---------------------------------------------
# datas_1 = []#存放旧随机8个排序组合 datas_2=[]#存放新随机8个排序组合
datas_1v = sorttsp(datas_1,data)
datas_2v = sorttsp(datas_2,data)
datas_1 = value_data(datas_1v, datas_1)
datas_2 = value_data(datas_2v, datas_2)
datas_1v.sort()
datas_2v.sort()
for ii in range(len(datas_1v)):
if ii == 0:#记录每次最优路径
if datas_1v[ii] <= datas_2v[ii]:
#判断是否为第一次比较,防止数据未被放入all_raod或者all_value中
if datas_1v[ii] not in all_value:
all_value.append(datas_1v[ii])
for iii in datas_1:
if iii[-1]==datas_1v[ii]:
all_road.append(iii)
i = i + 1
else:#datas_1v[ii] > datas_2v[ii]
all_value.append(datas_2v[ii])
for iii in datas_2:
if iii[-1] == datas_2v[ii]:
all_road.append(iii)
i = 0
if ii == 0 and i == 10:
for iii in range(len(datas_1)):
if datas_1[iii][-1] == datas_1v[ii]:
i_i = iii
i_i_value=datas_1v[ii]
if datas_1v[ii] > datas_2v[ii]: # 旧的>新的
for iii in range(len(datas_1)):
if datas_1[iii][-1] == datas_1v[ii]:#是datas_1[iii][-1] == datas_1v[ii],而不是datas_1[iii][-1] == datas_2v[ii]
for iiii in range(len(datas_2)):#注意逻辑,要旧值对应的行=新值对应的行
if datas_2[iiii][-1] == datas_2v[ii]:
datas_1[iii]=datas_2[iiii]
for ii in range(len(datas_1)):
datas_1[ii].remove(datas_1[ii][-1])
# ------------结束全部工作,输出最优排序-------------------------------------------------------
#all_value = [] # 存放所有value
#all_road = [] # 存放所有路径
for i_r_v in range(0,len(all_value)):
print('局部最优路径和值:',all_road[i_r_v],'到起点',all_road[i_r_v][0])
print('局部最优路径值:',all_value[i_r_v])
print('----------------最终最优路径和值:----------------------------------------')
print(datas_1[i_i],'到起点',datas_1[i_i][0],'value=',i_i_value)
#变量名要起好,不然重复会导致错误
if __name__ == "__main__":
data = openreadtxt('data.txt')#读取全部点坐标
indata = indata(data)#选择点路径
yichuan(indata, data)#遗传算法