广州大学人工智能原理实验四:TSP问题的遗传算法实现

相关资料

广州大学人工智能原理实验一:知识的表示与推理实验
广州大学人工智能原理实验二:八数码问题
广州大学人工智能原理实验三:产生式系统推理
广州大学人工智能原理实验四:TSP问题的遗传算法实现
广州大学人工智能原理实验五:基于汉诺塔的问题规约图实现
五份实验报告下载链接🔗

一、实验目的

本实验课程是计算机、智能、物联网等专业学生的一门专业课程,通过实验,帮助学生更好地掌握人工智能相关概念、技术、原理、应用等;通过实验提高学生编写实验报告、总结实验结果的能力;使学生对智能程序、智能算法等有比较深入的认识。要掌握的知识点如下:
1.掌握人工智能中涉及的相关概念、算法;
2.熟悉人工智能中的知识表示方法;
3.掌握问题表示、求解及编程实现;
4.熟悉和掌握遗传算法的基本概念和基本思想;
5.理解和掌握遗传算法的各个操作算子,能够用选定的编程语言设计简单的遗传优化系统;
6.通过实验培养学生利用遗传算法进行问题求解的基本技能。

二、基本要求

1.实验前,复习《人工智能》课程中的有关内容。
2.准备好实验数据。
3.程序可以在多个同学交流下讨论完成,但是实验报告独立完成。
4.该按照自己完成的部分进行。

三、实验软件

推荐使用C或C++(Visual studio等平台)(不限制语言使用,Java,matlab,Python等)。

四、实验内容:

以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 8 9 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)

有人求得的最优路径为: 1 4 6 5 10 9 8 7 3 2 1
路径长度是166.541336
上述10个城市的求解中编号从0开始,把所有路径搜索完又返回到出发节点。

2 问题描述
应用遗传算法求解30/10个节点的TSP(旅行商问题)问题,求问题的最优解。

五、实验报告内容

(1)要求求出问题最优解,若得不出最优解,请分析原因;
一、取前10个城市测试:
参数设置:

交叉率突变率种群大小进化代数
0.50.130300

最优路径为:
在这里插入图片描述
[ 1 2 3 7 8 9 10 5 6 4 1]
最优距离:166.54133557746266

二、取所有城市测试:
参数设置:

交叉率突变率种群大小进化代数
0.60.32001000

最优路径为:在这里插入图片描述
[13 12 11 10 9 8 7 5 6 4 3 2 1 30 29 27 26 28 25 24 23 22 21 20 18 19 17 16 15 14 13]
最优距离:424.8692923184516

(2)对实验中的几个算法控制参数进行仔细定义,并能通过实验选择参数的最佳值;

① 以所有城市测试为例,进行以下参数讨论;
② 每一条基因的定义为一条旅游路线,即每一条基因对应了30个城市数字下标,这30个数字每个都是有且只有一个;
③ 选择种群:优胜劣汰,对低于平均适应度的染色体进行交叉和突变,高于平均适应度的进行保留;
④ 交叉操作:选取两条父母染色体,对母亲染色体随机截取一段S,对父染色体在S上出现的数字剔除,然后拼接S,返回交叉后的染色体;
⑤ 突变操作:对染色体进行随机截取一段,进行翻转操作;
⑥ 种群进化:种群内每一根染色体都有可能进行交叉和突变,取决于交叉率和突变率。
⑦ 大的种群可以保证每一代里拥有的染色体更多,交叉和突变得到的新染色体更多,有助于跳出局部最优解,尽早获得全局最优解。
⑧ 为了快速得到实验结果,默认进化代数为500、种群大小为100,对交叉率和突变率进行消融实验

表一:消融实验
pc\pm0.10.20.30.40.50.60.70.80.91.0
0.1498.7467.7465.8458.5468.7476428.9511.1444548.1
0.2459.9470.3470.5446.2457.9450.9440.5500.5466.2477.7
0.3465.9460.4513.5463.4428.7488.6431.8477.4532.3534.3
0.4506453.9424.9498.4461.1487.3458510.5548.6593.9
0.5484.3447.1448.7489.9471.5437533.9552.7538597.1
0.6445.6488429.1482.2445.6500.1445620.5588.6609.6
0.7439.9451.6429.1495.4521.5570574.9627.9637.8618.9
0.8440.7454.7499.7559.4594.1578.2571.7684.5673.1668.6
0.9504.8536.6590.2559.8659.7669.5641.6720.2682.7677.9
1.0561.5625612.4659.1638.5688.1741.9767.3792.5736.3

⑨ 可以看到pc取0.4,pm取0.3时,可取得全局最优解。但实验初始化具有随机性,参数的选择仅具有一定参考意义。
⑩ 解决办法:采用随机初始化种子,固定随机数的产生,进行多组消融实验,因花费时间可能过多,这里不再一一展开。

(3)要求界面显示每次迭代求出的局部最优解和最终求出的全局最优解。

一、取前10个城市测试:
在这里插入图片描述

二、取所有城市测试:
在这里插入图片描述

(4)实验结果讨论。

① 本次实验,前十城市还是所有城市,都可以成功寻得全局最优值,完成了实验的基本要求。
② 遗传算法的核心主要在于,种群淘汰、交叉与变异三个操作,因为这几个点的选择非常重要,所以要针对具体问题设计对应的操作,这样可以大大加快优化的时间。
③ 选择合适的操作之后,还要对参数进行设置,种群大小、交叉率、变异率,这三个参数可以直接影响是否能够跳出局部最优,寻得全局最优。
④ 在对于所有城市的求解过程中,可以发现全局最优解的获得往往要通过多次运行代码才能获得,因此对于该遗传算法应该仍有较大的优化空间。
⑤ 通过本次实验,可以发现一个种群存在着一些问题,就是每次保留的最优染色体都一直是由该种群产生的,很容易陷入局部最优解,或许可以采用多个种群解决这一问题。
⑥ 通过网上查阅,发现已经有人提出了多种群遗传算法MSGA,下面为多种群遗传算法流程简介图。
在这里插入图片描述

图一:多种群遗传算法流程图
⑦ 具体操作为:将初始种群展开成N个种群,然后同时进行进化(SGA,标准的遗传算法),然后在各个种群选出最优的,合成一个精华种群,然后反复迭代更新,可以设置一个早停参数M,避免运行时间过长。

六、实验代码

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
from matplotlib.font_manager import FontProperties




class YIchuan(object):
best_distance = -1  # 记录目前最优距离
    best_gene = []  # 记录目前最优旅行方案
    all_best_distance = [] #记录每一代最优距离
    citys = np.array([])  # 城市数组
    citys_name = np.array([])
    population_size = 100  # 种群大小,每个种群含有多少条基因
    cross_rate = 0.9  # 交叉率
    change_rate = 0.1  # 突变率
    population = np.array([])  # 种群数组
    fitness = np.array([])  # 适应度数组
    city_size = -1  # 标记城市数目
    iter_num = 200  # 最大迭代次数
    

    def __init__(self, cross_rate, change_rate, population_size, iter_num):
        self.fitness = np.zeros(self.population_size)
        self.cross_rate = cross_rate
        self.change_rate = change_rate
        self.population_size = population_size
        self.iter_num = iter_num
        self.fig, self.ax = plt.subplots()
        self.plt = plt

    def init(self):
        TSP = self
        TSP.load_city_data()    # 加载城市数据
        TSP.population = TSP.creat_population(TSP.population_size)  # 创建种群
        TSP.fitness = TSP.get_fitness(TSP.population)  # 计算初始种群适应度
        self.ax.axis([0, 100, 0, 100])


    def creat_population(self, size):
        """
        创建种群
        :param size:种群大小
        :return: 种群
        """
        population = [] # 存储种群生成的基因
        for i in range(size):
            gene = np.arange(self.citys.shape[0])
            np.random.shuffle(gene)  # 打乱数组[0,...,city_size]
            population.append(gene)  # 加入种群
        return np.array(population)

    def get_fitness(self, population):
        """
        获得适应度
        :param population:种群
        :return: 种群每条基因对应的适应度
        """
        fitness = np.array([])  # 适应度记录数组
        for i in range(population.shape[0]):
            gene = population[i]  # 取其中一条基因(编码解,个体)
            dis = self.compute_distance(gene)  # 计算此基因优劣(距离长短)
            dis = self.best_distance / dis  # 当前最优距离除以当前population[i](个体)距离;越近适应度越高,最优适应度为1
            fitness = np.append(fitness, dis)  # 保存适应度population[i]
        return fitness


    def select_population(self, population):
        """
        选择种群,优胜劣汰
        策略:低于平均的要替换
        :param population: 种群
        :return: 更改后的种群
        """
        best_index = np.argmax(self.fitness)
        ave = np.median(self.fitness, axis=0)
        for i in range(self.population_size):
            if i != best_index and self.fitness[i] < ave:
                pi = self.cross(population[best_index], population[i]) #交叉
                pi = self.change(pi) #变异
                population[i, :] = pi[:]

        return population


    def cross(self, parent1, parent2):
        """
        交叉
        :param parent1: 父亲
        :param parent2: 母亲
        :return: 儿子基因
        """
        if np.random.rand() > self.cross_rate:
            return parent1
        index1 = np.random.randint(0, self.city_size - 1)
        index2 = np.random.randint(index1, self.city_size - 1)
        tempgene = parent2[index1:index2]  # 交叉的基因片段
        newgene = []
        xxx = 0
        for g in parent1:
            if xxx == index1:
                newgene.extend(tempgene)  # 插入基因片段
            if g not in tempgene:
                newgene.append(g)
            xxx += 1
        newGene = np.array(newgene)

        return newGene

    def reverse_gene(self, gene, i, j):
        """
        翻转i到j位置的基因
        :param gene: 基因
        :param i: 第i个位置
        :param j: 第j个位置
        :return: 翻转后的基因
        """
        if i >= j:
            return gene
        if j > self.city_size - 1:
            return gene
        parent1 = np.copy(gene)
        tempgene = parent1[i:j]
        newgene = []
        p1len = 0
        for g in parent1:
            if p1len == i:
                newgene.extend(tempgene[::-1])  # 插入基因片段
            if g not in tempgene:
                newgene.append(g)
            p1len += 1
        return np.array(newgene)

    def change(self, gene):
        """
        突变,主要使用翻转
        :param gene: 基因
        :return: 突变后的基因
        """
        if np.random.rand() > self.change_rate:
            return gene
        index1 = np.random.randint(0, self.city_size - 1)
        index2 = np.random.randint(index1, self.city_size - 1)
        new_gene = self.reverse_gene(gene, index1, index2)  #翻转
        return new_gene

    def evolution(self):
        """
        迭代进化种群
        :return: None
        """
        for i in range(self.iter_num):
            best_index = np.argmax(self.fitness)
            worst_f_index = np.argmin(self.fitness)
            local_best_genee = self.population[best_index]
            local_best_distance = self.compute_distance(local_best_genee)
            if i == 0:  #第一代记录最优基因和最短距离
                self.best_gene = local_best_genee
                self.best_distance = self.compute_distance(local_best_genee)

            if local_best_distance < self.best_distance:
                self.best_distance = local_best_distance  # 记录最优值
                self.best_gene = local_best_genee  # 记录最个体基因
            else:
                self.population[worst_f_index] = self.best_gene  #替换掉最差的基因
            print('代数:%d 最优距离:%s' % (i, self.best_distance))
            self.all_best_distance.append(self.best_distance)

            self.population = self.select_population(self.population)  # 选择淘汰种群
            self.fitness = self.get_fitness(self.population)  # 计算种群适应度
            for j in range(self.population_size):
                k = np.random.randint(0, self.population_size - 1)
                if j != k:
                    self.population[j] = self.cross(self.population[j], self.population[k])  # 交叉种群中第j,k个体的基因
                    self.population[j] = self.change(self.population[j])  # 突变种群中第j个体的基因


    def load_city_data(self, file='city.csv', delm=';',head = 30):
        # 加载实验数据
        data = pd.read_csv(file, delimiter=delm, header=None).values
        self.citys = data[:head, 1:]
        self.citys_name = data[:head, 0]
        self.city_size = data[:head].shape[0]



    def compute_distance(self, gen):
        # 计算该基因的总距离
        distance = 0.0
        for i in range(-1, len(self.citys) - 1):
            index1, index2 = gen[i], gen[i + 1]
            city1, city2 = self.citys[index1], self.citys[index2]
            distance += np.sqrt((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2)
        return distance


    def compute_oushi_distance(self, city1, city2):
        # 计算两个地点之间的欧氏距离
        dis = np.sqrt((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2)
        return dis

    def draw_line(self, from_city, to_city):
        """
        连线
        :param from_city: 城市来源
        :param to_city: 目的城市
        :return: none
        """
        line1 = [(from_city[0], from_city[1]), (to_city[0], to_city[1])]
        (line1_xs, line1_ys) = zip(*line1)
        self.ax.add_line(Line2D(line1_xs, line1_ys, linewidth=1, color='blue'))


    def draw_citys_way(self, gene):
        '''
        根据一条基因绘制一条旅行路线
        :param gene:
        :return:none
        '''
        num = gene.shape[0]
        self.ax.axis([0, 100, 0, 100])
        for i in range(num):
            if i < num - 1:
                best_i = self.best_gene[i]
                next_best_i = self.best_gene[i + 1]
                best_icity = self.citys[best_i]
                next_best_icity = self.citys[next_best_i]
                self.draw_line(best_icity, next_best_icity)
        start = self.citys[self.best_gene[0]]
        end = self.citys[self.best_gene[-1]]
        self.draw_line(end, start)

    def draw_citys_name(self, gen, size=5):
        '''
        根据一条基因gen绘制对应城市名称
        :param gen:
        :param size: text size
        :return:
        '''
        m = gen.shape[0]
        self.ax.axis([0, 100, 0, 100])
        for i in range(m):
            c = gen[i]
            best_icity = TSP.citys[c]
            self.ax.text(best_icity[0], best_icity[1], TSP.citys_name[c], fontsize=10)

    def draw(self):
        """
        绘制最终结果
        :return: none
        """
        self.ax.plot(self.citys[:, 0], self.citys[:, 1], 'ro')
        self.draw_citys_name(self.population[0], 8)
        self.draw_citys_way(self.best_gene)
        plt.show()

    def draw_dis(selfs):
        """
        绘制最优距离曲线
        :return: none
        """
        x = [i for i in range(len(selfs.all_best_distance))]
        y = selfs.all_best_distance

        fig, ax = plt.subplots(figsize=(15, 10))
        font = FontProperties(fname=r"/System/Library/Fonts/STHeiti Medium.ttc", size=20)
        ax.plot(x, y, color='darkblue', linestyle='-')

        plt.xlabel("进化代数", FontProperties=font, size=30)  # X轴标签
        plt.ylabel("最优解变化", FontProperties=font, size=30)  # Y轴标签
        plt.title("遗传算法", FontProperties=font, size=30)
        plt.grid()
        plt.show()


if __name__ == '__main__':
    """消融实验
    number = np.ones([10,10])
    for i in range(10):
        for j in range(10):
            TSP = YIchuan(0.1*(i+1), 0.1*(j+1), 100, 500)  # 交叉率、突变率、种群大小、进化代数
            TSP.init()
            TSP.evolution()
            TSP.draw()
            TSP.draw_dis()
            print("最优路径为:")
            print(np.append(TSP.best_gene, TSP.best_gene[0]) + 1)
            number[i,j] = TSP.best_distance

    number = pd.DataFrame(number)
    number.to_csv('out.csv')
    """
    TSP = YIchuan(0.4, 0.3, 200, 1000)  # 交叉率、突变率、种群大小、进化代数
    TSP.init()
    TSP.evolution()
    TSP.draw()
    TSP.draw_dis()
    print("最优路径为:")
    print(np.append(TSP.best_gene,TSP.best_gene[0])+1)
  • 10
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
TSP(Traveling salesman problem)问题是指给定一系列城市和每对城市之间的距离,求解访问每一座城市恰好一次并回到起始城市的最短回路。由于TSP问题是NP-hard问题,因此遗传算法是一种比较有效的解决方法之一。 遗传算法是一种模拟自然选择和遗传机制的优化算法。在TSP问题中,可以将每个可能的解(即每个访问城市的顺序)看作个体,采用遗传算法对这些个体进行进化,以找到最优解。具体来说,遗传算法的主要步骤包括以下几个方面: 1. 初始化:随机生成一组个体作为种群,每个个体都代表一种可能的解。 2. 评估:计算每个个体的适应度,即其对应的回路长度。 3. 选择:根据个体的适应度选择一些优秀的个体作为父代参与进化。 4. 交叉:对父代个体进行随机交叉操作,生成新的子代个体。 5. 变异:对子代个体进行随机变异操作,引入新的解。 6. 替换:根据适应度选择一些优秀的个体作为种群中的新个体,替换不优秀的个体。 7. 结束条件:达到预设的迭代次数或者找到满意的解时停止进化。 需要注意的是,在TSP问题中,遗传算法的评估过程需要计算每个个体的回路长度,这是一个比较耗时的操作。因此,可以采用一些启发式算法来加速计算,例如最近邻算法、2-opt算法等。 总的来说,采用遗传算法来解决TSP问题是一种比较可行的方法,但是需要注意调整算法参数,以获得更好的解。同时,也可以结合其他优化算法来提高解决效率和精度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wujiekd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值