遺傳演算法解決尋路問題——Python描述
阿新 • • 發佈:2018-11-03
概要
我的上一篇寫遺傳演算法解決排序問題,當中思想借鑑了遺傳演算法解決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)