概要
我的上一篇写遗传算法解决排序问题,当中思想借鉴了遗传算法解决TSP问题,本质上可以认为这是一类问题,就是这样认为:寻找到一个序列X,使F(X)最大。
详解介绍
排序问题:寻找一个序列,使得这个序列的逆序对的倒数最大。
TSP问题:寻找一个序列,使得这个序列的总路径长的倒数最大。
这两个问题有一个共同的特点是,所有的节点都要用上,而使用遗传算法解决排序问题(每一个格子可以认为是一个节点),是需要从众多的节点之中寻找到某些节点构成一个序列X。
序列X必须满足的条件是:
- 相邻节点直接邻接
- 无重复节点(有重复的相当于走回头路)
- 序列的起点和终点必须是已知的点
第一个需要解决的问题是初代如何选择:
- 随机选择然后判断是否符合上面的三个条件(垃圾)
- 从起点开始随机生成到终点的序列
第二种做法的另一个问题就是随机性太大,可能会走比较长的路(其实也是可以采用的),为了解决这个问题,我才用了A*算法的启发式思维,将当前点和目标点的蔓哈顿距离作为适应度加入到优先队列中。
算法步骤
- 将起点加入到优先队列中
- 从优先队列中取出顶部顶点p0,将p0加入到Path(路径结果),如果p0是终点结束;
- 随机获取其周围的8个点中的一个p1
- 比较p0到目标点的曼哈顿距离|p0-target| 和p1到目标点的距离|p1-target|
- 如果|p1-target|<|p0-target|并且p1 not in Path, 将p1加入优先队列,p0<-p1;转到2
使用这种策略不仅引入了随机性,而且路径也比较合适,收敛比较快。
选择
这一步比较简单,就是普通的轮盘法就ok
交叉和变异
目前还没有想到策略(后面补充)
代码实现
1 import random 2 import math 3 4 MIN = 0 5 MAX = 9999 6 WIDTH = 100 7 HEIGHT = 100 8 PATH_COUNT = 100 9 10 DIS_1 = 1 / math.sqrt(2) 11 DIS_2 = 1 12 13 S = 0 14 D = 0 15 # 路径 16 paths = [] 17 # 最优路径 18 best_path = [] 19 # 迭代次数 20 ITERATION_COUNT = 10 21 # 22 direction_arr = [(-1, -1), (0, -1), (1, -1), (-1, 0), (1, 0), (-1, 1), (0, 1), (1, 1)] 23 24 25 def is_valid(point): 26 if point[0] < 0 or point[1] < 0 or point[0] >= WIDTH or point[1] >= HEIGHT: 27 return False 28 return True 29 30 31 # 计算欧式距离 32 def distance(p1, p2): 33 return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) 34 35 36 # 标号转坐标 37 def mark2position(mark): 38 return (mark % WIDTH, int(mark / WIDTH)) 39 40 41 def position2mark(position): 42 return position[1] * WIDTH + position[0] 43 44 45 # 5 6 7 46 # 3 4 47 # 0 1 2 48 def generate_one_path(start, end): 49 res = [] 50 res.append(start) 51 52 s = start 53 target_point = mark2position(end) 54 dis = distance(mark2position(start), target_point) 55 56 while (s != end): 57 pos = mark2position(s) 58 r = random.randint(0, 7) 59 pos = (pos[0] + direction_arr[r][0], pos[1] + direction_arr[r][1]) 60 temp_dis = distance(pos, target_point) 61 if is_valid(pos) and temp_dis <= dis: 62 s = position2mark(pos) 63 dis = temp_dis 64 res.append(s) 65 return res 66 67 68 # 初代 69 def init(count): 70 res = [] 71 for i in range(0, count): 72 res.append(generate_one_path(S, D)) 73 return res 74 75 76 # 计算一条路径的适应度值 77 def one_path_fit_val(path): 78 sm = 0 79 for i in range(1, len(path)): 80 w = int(math.fabs(path[i - 1] - path[i])) 81 if w == 1 or w == WIDTH: 82 sm += DIS_2 83 else: 84 sm += DIS_1 85 return sm 86 87 88 # 计算适应度值 89 def fitness(): 90 res = [] 91 max_fit = -1 92 for path in paths: 93 f = one_path_fit_val(path) 94 if f > max_fit: 95 max_fit = f 96 best_path = path 97 res.append(f) 98 return res 99 100 101 # 累计概率 102 def cumulative_probability(fits): 103 res = [] 104 sm = sum(fits) 105 temp = fits[0] / sm 106 res.append(temp) 107 for i in range(1, len(fits)): 108 res.append(res[i - 1] + fits[i] / sm) 109 return res 110 111 112 # 选择 产生下一代 113 def choose(pArr, count): 114 res = [] 115 for i in range(count): 116 p = random.random() 117 for j in range(len(pArr)): 118 if p <= pArr[j]: 119 res.append(paths[j]) 120 break 121 return res 122 123 124 # run point 125 random.seed() 126 S = random.randint(MIN, MAX) 127 D = random.randint(MIN, MAX) 128 while (S == D): 129 D = random.randint(MIN, MAX) 130 131 # 初代 132 paths = init(PATH_COUNT) 133 134 fits = fitness() # 适应度计算 135 pArr = cumulative_probability(fits) # 累计概率 136 res = choose(pArr, PATH_COUNT) # 选择 137 138 for p in res: 139 print(p)