GA求解TSP

遗传算法中包含了如下5个基本要素:

(1)对参数进行编码;

(2)设定初始种群大小;

(3)适应度函数的设计;

(4)遗传操作设计;

(5)控制参数设定(包括种群大小、最大进化代数、交叉率、变异率等)。

下面使用python编程对中国28个省会城市的TSP问题进行了求解,python的版本是2.7.14。百度地图提供了JavaScript API,能够很方便地获取各城市之间的路径距离。这里有一段获取28个城市之间距离的JS代码,将其复制到百度地图JavaScript API的一些示例网页上,然后点击运行,再点击右侧相应的按钮即可获取各城市之间的距离。

 1 <html>
 2 <head>
 3     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 4     <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
 5     <style type="text/css">
 6         body, html{width: 100%;height: 100%;overflow: hidden;margin:0;font-family:"微软雅黑";}
 7         #l-map{height:100px;width:100%;}
 8         #r-result{width:100%; font-size:1px;line-height:12px;}
 9     </style>
10     <script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=您的密钥"></script>
11     <title>计算驾车时间与距离</title>
12 </head>
13 <body>
14     <div id="l-map"></div>
15     <div id="r-result">
16         <input type="button" value = "计算各省会城市间距离矩阵" onclick = "getCityDistance()" />
17         <div id="result"></div>
18     </div>
19 </body>
20 </html>
21 <script type="text/javascript">
22     // 百度地图API功能
23     var map = new BMap.Map("l-map");
24     map.centerAndZoom(new BMap.Point(116.404, 39.915), 5);
25     var city = [
26         "北京",  "长春",  "沈阳", "哈尔滨", "石家庄", "济南",
27         "呼和浩特", "太原", "郑州", "南京",  "上海",  "杭州",
28         "福州",    "南昌", "武汉", "合肥",  "长沙",  "广州",
29         "重庆",    "西安", "海口", "贵阳",  "昆明",  "成都",
30         "兰州",    "西宁", "拉萨", "乌鲁木齐"
31     ];
32 
33     var i = 0, j = 1;
34 
35     function getCityDistance()
36     {
37         setInterval(getCityDistance1, 6000);
38     }
39     
40     function getCityDistance1()
41     {
42         if(i >= city.length - 1)
43         {
44             return;
45         }
46                     var output = "";
47                     city1 = city[i];
48                     city2 = city[j];
49                     var searchComplete = function (results)
50                     {
51                         if(transit.getStatus() != BMAP_STATUS_SUCCESS)
52                         {
53                             return ;
54                         }
55                         var plan = results.getPlan(0);
56                          output += plan.getDistance(false) + ", ";                    //获取距离
57                         document.getElementById("result").innerHTML += output;
58                     }
59                     var transit = new BMap.DrivingRoute(
60                                                         map,
61                                                         {renderOptions: {map: map, autoViewport: false}, onSearchComplete: searchComplete, onPolylinesSet: function(){}}
62                                                         );
63                 
64                     transit.search(city1, city2);
65                 
66                 j++;
67                 if(j >= city.length)
68                 {
69                     i++;
70                     j = i + 1;
71                 }
72 
73     }
74 </script>
View Code

第一步,初始化种群

    # 创造生命集
    for i in range(self.life_count):
        self.lives.append(Life(self.make_life()))
    # 创造新生命
    def make_life(self):
        lst = range(self.gene_length)
        # 随机顺序
        random.shuffle(lst)
        return lst

第二步,计算个体适应度

使用每个个体对应的路线总长度的倒数作为个体的适应度,也就是每个个体的得分

    # 评价函数
    def judge(self):
        self.bounds = 0.0
        self.best = Life()
        self.best.setScore(-1.0)
        for lf in self.lives:
            lf.score = 1.0/self.distance(lf.gene)
            if lf.score > self.best.score:
                self.best = lf
            self.bounds += lf.score
    # 得到当前顺序下连线总长度
    def distance(self, order):
        distance = 0
        for i in range(-1, self.gene_length - 1):
            i1, i2 = order[i], order[i + 1]
            distance += self.get_city_distance(self.dist_matrix, self.gene_length, i1, i2)
        return distance

第三步,更新种群

(1)将种群中适应度最高(得分最高)的个体加入下一代种群中

(2)从种群中选择一些个体用于产生下一代,每个个体被选到的可能性与它的得分成正比。选择概率计算方式为

(3)从种群中选择两个个体后,以一定的概率进行交叉。交叉的方法为:从第二个个体基因的前段截取一段随机长度的基因接到第一个个体基因的前面,然后剔除其与截取基因段中重复的元素,最终得到一个新的基因

(4)以一定的概率对第(3)步得到的基因进行变异。变异的方法为:从基因中随机选取两个位置,将这两个位置的元素互换,并将其中一个元素转移到基因的末尾

(5)重复(2)到(4),直到产生的新个体数与原种群个体数相同为止

    # 演化至下一代
    def next(self):
        # 评估群体
        self.judge()
        # 新生命集
        newLives = []
        # 将最好的父代加入竞争
        newLives.append(Life(self.best.gene))
        # 产生新的生命集个体
        while(len(newLives) < self.life_count):
            newLives.append(self.bear())
        # 更新新的生命集
        self.lives = newLives
        self.generation += 1
    # 产生后代
    def bear(self):
        lf1 = self.get_onelife()
        lf2 = self.get_onelife()
        # 交叉
        r = random.random()
        if r < self.overlapping_rate:
            gene = self.overlapping_func(lf1, lf2)
        else:
            gene = lf1.gene
        # 突变
        r = random.random()
        if r < self.mutation_rate:
            gene = self.mutation_func(gene)
            self.mutation_count += 1
        # 返回生命体
        return Life(gene)
    # 根据得分情况,随机取得一个个体,机率正比于个体的score属性
    def get_onelife(self):
        # 轮盘
        r = random.uniform(0, self.bounds)
        for lf in self.lives:
            r -= lf.score;
            if r <= 0:
                return lf
    # 交叉函数:选择lf2序列前子序列交叉到lf1前段,删除重复元素
    def overlapping_func(self, lf1, lf2):
        p2 = random.randint(1, self.gene_length - 1)
        # 截取if2
        g1 = lf2.gene[0:p2] + lf1.gene
        g11 = []
        for i in g1:
            if i not in g11:
                g11.append(i)
        return g11
    # 变异函数:选择两个不同位置基因交换,第一个选择的基因重新加入到序列尾端
    def mutation_func(self, gene):
        p1 = random.randint(0, self.gene_length - 1)
        p2 = random.randint(0, self.gene_length - 1)
        while p2 == p1:
            p2 = random.randint(0, self.gene_length - 1)
        gene[p1], gene[p2] = gene[p2], gene[p1]
        gene.append(gene[p2])
        del gene[p2]
        return gene

完整的代码如下

  1 # -*- coding: utf-8 -*-
  2 
  3 import random
  4 import math
  5 
  6 class Life(object):
  7 
  8     # 初始化
  9     def __init__(self, gene = None):
 10         self.gene = gene
 11         self.score = -1.0
 12 
 13     # 设置评估分数
 14     def setScore(self, v):
 15         self.score = v
 16 
 17 
 18 #----------- TSP问题 -----------
 19 class MyTSP(object):
 20 
 21     # 初始化
 22     def __init__(self, gene_length = 28, overlapping_rate = 0.7, mutation_rate = 0.03, life_count = 30, max_iteration = 4000):
 23 
 24         self.overlapping_rate = overlapping_rate                                  # 交叉率
 25         self.mutation_rate = mutation_rate                    # 突变率
 26         self.mutation_count = 0                              # 突变次数
 27         self.generation = 0                                 # 进化代数
 28         self.lives = []                                     # 生命集合
 29         self.bounds = 0.0                                   # 得分总数    
 30         self.best = None                                    # 种群每代中的最优个体
 31         self.best_gene = []                                 # 所有代种群中的最优基因
 32         self.best_gene_distance = 1e20                      # 所有代种群中的最优基因所对应的路径长度
 33         self.life_count = life_count                          # 生命个数
 34         self.gene_length = gene_length                        # 基因长度,此处即为城市个数
 35         self.max_iteration = max_iteration
 36         self.min_distance = []
 37 
 38         # 创造生命集
 39         for i in range(self.life_count):
 40             self.lives.append(Life(self.make_life()))
 41 
 42         # 读取各城市间的距离
 43         with open('distance.txt', 'r') as f:
 44             data = f.read()
 45             data = data.replace(",", "")
 46             odom = data.split()
 47             self.dist_matrix = map(float, odom)
 48 
 49         print self.lives[6].gene
 50         print self.distance(self.lives[6].gene)
 51 
 52      # 进化计算
 53     def evolve(self):
 54         while self.generation < self.max_iteration:
 55             # 下一步进化
 56             self.next()
 57             self.min_distance.append(self.distance(self.best.gene))
 58             if self.distance(self.best.gene) < self.best_gene_distance:
 59                 self.best_gene_distance = self.distance(self.best.gene)
 60                 self.best_gene = self.best.gene
 61             if self.generation % 200 == 0:
 62                 print("迭代次数:%d, 变异次数%d, 最佳路径总距离:%d" % (self.generation, self.mutation_count, self.distance(self.best.gene)))
 63         print self.best_gene
 64         print self.best_gene_distance
 65         return
 66 
 67     # 创造新生命
 68     def make_life(self):
 69         lst = range(self.gene_length)
 70         # 随机顺序
 71         random.shuffle(lst)
 72         return lst
 73 
 74     # 演化至下一代
 75     def next(self):
 76         # 评估群体
 77         self.judge()
 78         # 新生命集
 79         newLives = []
 80         # 将最好的父代加入竞争
 81         newLives.append(Life(self.best.gene))
 82         # 产生新的生命集个体
 83         while(len(newLives) < self.life_count):
 84             newLives.append(self.bear())
 85         # 更新新的生命集
 86         self.lives = newLives
 87         self.generation += 1
 88 
 89     # 评价函数
 90     def judge(self):
 91         self.bounds = 0.0
 92         self.best = Life()
 93         self.best.setScore(-1.0)
 94         for lf in self.lives:
 95             lf.score = 1.0/self.distance(lf.gene)
 96             if lf.score > self.best.score:
 97                 self.best = lf
 98             self.bounds += lf.score
 99 
100     # 得到当前顺序下连线总长度
101     def distance(self, order):
102         distance = 0
103         for i in range(-1, self.gene_length - 1):
104             i1, i2 = order[i], order[i + 1]
105             distance += self.get_city_distance(self.dist_matrix, self.gene_length, i1, i2)
106         return distance
107 
108     def get_city_distance(self, dist_matrix, n, i, j):
109         if i == j:
110             return 0
111         elif i > j:
112             i, j = j, i
113         return dist_matrix[i * (2 * n - i - 1)/2 + j - i - 1]
114 
115     # 产生后代
116     def bear(self):
117         lf1 = self.get_onelife()
118         lf2 = self.get_onelife()
119         # 交叉
120         r = random.random()
121         if r < self.overlapping_rate:
122             gene = self.overlapping_func(lf1, lf2)
123         else:
124             gene = lf1.gene
125         # 突变
126         r = random.random()
127         if r < self.mutation_rate:
128             gene = self.mutation_func(gene)
129             self.mutation_count += 1
130         # 返回生命体
131         return Life(gene)
132 
133     # 根据得分情况,随机取得一个个体,机率正比于个体的score属性
134     def get_onelife(self):
135         # 轮盘
136         r = random.uniform(0, self.bounds)
137         for lf in self.lives:
138             r -= lf.score;
139             if r <= 0:
140                 return lf
141 
142     # 交叉函数:选择lf2序列前子序列交叉到lf1前段,删除重复元素
143     def overlapping_func(self, lf1, lf2):
144         p2 = random.randint(1, self.gene_length - 1)
145         # 截取if2
146         g1 = lf2.gene[0:p2] + lf1.gene
147         g11 = []
148         for i in g1:
149             if i not in g11:
150                 g11.append(i)
151         return g11
152 
153     # 变异函数:选择两个不同位置基因交换,第一个选择的基因重新加入到序列尾端
154     def mutation_func(self, gene):
155         p1 = random.randint(0, self.gene_length - 1)
156         p2 = random.randint(0, self.gene_length - 1)
157         while p2 == p1:
158             p2 = random.randint(0, self.gene_length - 1)
159         gene[p1], gene[p2] = gene[p2], gene[p1]
160         gene.append(gene[p2])
161         del gene[p2]
162         return gene
163 
164 
165 import matplotlib.pyplot as plt
166 
167 overlapping_rate = [0.1, 0.2, 0.3, 0.4, 0.5, 0.7, 0.8, 0.9, 1.0]
168 mutation_rate = [0.005, 0.01, 0.03, 0.05, 0.07, 0.1, 0.2, 0.4, 0.7, 1.0]
169 life_count = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
170 yy = []
171 
172 for i in range(len(overlapping_rate)):
173     tsp = MyTSP(gene_length = 28, overlapping_rate = overlapping_rate[i], mutation_rate = 0.03, life_count = 30, max_iteration = 40)
174     tsp.evolve()
175     yy.append(tsp.best_gene_distance)
176 plt.figure('overlapping rate and minimum distance')
177 plt.plot(overlapping_rate, yy)
178 plt.xlabel('overlapping rate')
179 plt.ylabel("minimum distance of all generations")
180 
181 yy = []
182 for i in range(len(mutation_rate)):
183     tsp = MyTSP(gene_length = 28, overlapping_rate = 0.7, mutation_rate = mutation_rate[i], life_count = 30, max_iteration = 40)
184     tsp.evolve()
185     yy.append(tsp.best_gene_distance)
186 plt.figure("mutation rate and minimum distance")
187 plt.plot(mutation_rate, yy)
188 plt.xlabel('mutation rate')
189 plt.ylabel("minimum distance of all generations")
190 
191 yy = []
192 for i in range(len(life_count)):
193     tsp = MyTSP(gene_length = 28, overlapping_rate = 0.7, mutation_rate = 0.03, life_count = life_count[i], max_iteration = 40)
194     tsp.evolve()
195     yy.append(tsp.best_gene_distance)
196 plt.figure('life count of population and minimum distance')
197 plt.plot(life_count, yy)
198 plt.xlabel('life count of population')
199 plt.ylabel("minimum distance of all generations")
200 
201 yy = []
202 tsp = MyTSP(28, 0.5, 0.03, 30, 40)
203 tsp.evolve()
204 plt.figure('number of generation and minimum distance')
205 plt.plot(range(1, tsp.max_iteration + 1), tsp.min_distance)
206 plt.xlabel('number of generation')
207 plt.ylabel("minimum distance of every generation")
208 
209 plt.show()
View Code

代码中探究了迭代次数、交叉概率、变异概率、种群中生命体个数对求解效果的影响,分别如下面的4张曲线图

 

posted @ 2017-12-13 00:57  洗盏更酌  Views(1392)  Comments(0Edit  收藏  举报