前言
本文介紹了任務調度算的應用場景,算法分析,遺傳算法,國產2個優秀算法框架及實現旅行商問題的缺陷,最后根據遺傳算法原理編碼實現來規避缺陷
1 應用場景
任務調度時,有多達幾十種調度任務,有的任務不依賴上一條任務,有的任務只有在上一條任務執行完后才能執行,每條任務執行期間設備都可能會移動一段距離,並且設備只會在一個固定的區域移動。任務調度時,如何使設備移動具體最小?
問題延伸一下,這里的任務調度目標是設備移動距離最小,調度目標也有可能是總任務完成時間最短。根據以往經驗,從上一條任務執行到本任務都有一個時間,有開始時刻,結束時刻,可以構建一個n×n二維任務時刻矩陣,求任務完成時間最小值,等等。
上述問題非常典型,比如車間任務調度,巡檢任務調度等等!
2 算法分析
根據需求,設備總移動距離是目標函數,有的任務只有在上一條任務執行完后才能執行,為順序約束。這是一個排列組合問題,如果有n條任務,不考慮順序約束,有n!種組合,15!是1,307,674,368,000種組合,30!是265,252,859,812,191,058,636,308,480,000,000種組合。這么龐大的組合數量,不可能用傳統求和之后再比較得出最短距離。
針對上述分析結論,既然不能用傳統算法,那么就需要用到大數據智能優化搜索算法,這些算法有差分進化算法,遺傳算法,粒子群算法,模擬退火算法,蟻群算法,魚群算法等等。排列組合,路徑規划,是一個典型的旅行商問題,旅行商問題來源於遺傳算法,問題歸結於遺傳算法的旅行商問題
3 遺傳算法
遺傳算法(Genetic Algorithm,GA)最早是由美國的 John holland於20世紀70年代提出,該算法是根據大自然中生物體進化規律而設計提出的。是模擬達爾文生物進化論的自然選擇和遺傳學機理的生物進化過程的計算模型,是一種通過模擬自然進化過程搜索最優解的方法。該算法通過數學的方式,利用計算機仿真運算,將問題的求解過程轉換成類似生物進化中的染色體基因的交叉、變異等過程。在求解較為復雜的組合優化問題時,相對一些常規的優化算法,通常能夠較快地獲得較好的優化結果。遺傳算法已被人們廣泛地應用於組合優化、機器學習、信號處理、自適應控制和人工生命等領域
3.1 簡介
遺傳算法的起源可追溯到20世紀60年代初期。1967年,美國密歇根大學J. Holland教授的學生 Bagley在他的博士論文中首次提出了遺傳算法這一術語,並討論了遺傳算法在博弈中的應用,但早期研究缺乏帶有指導性的理論和計算工具的開拓。1975年, J. Holland等提出了對遺傳算法理論研究極為重要的模式理論,出版了專著《自然系統和人工系統的適配》,在書中系統闡述了遺傳算法的基本理論和方法,推動了遺傳算法的發展。20世紀80年代后,遺傳算法進入興盛發展時期,被廣泛應用於自動控制、生產計划、圖像處理、機器人等研究領域。
3.2 基本框架
3.2.1 編碼
3.2.2 適應度函數
3.2.3 初始群體選取
3.3 運算過程
3.3.1 選擇
3.3.2 交叉
3.2.3 變異
3.2.4 終止條件
3.3 特點
3.4 不足之處
3.5 應用
3.5.1 函數優化
3.5.2 組合優化
3.5.3 車間調度
4 智能搜索算法框架
智能搜索算法主流國產開源框架有geatpy和scikit-opt
4.1 geatpy
4.1.1 開源地址
https://github.com/geatpy-dev/geatpy
4.1.2 簡介
geatpy團隊現由華南農業大學、暨南大學、華南理工大學等優秀碩士以及一批優秀校友、優秀本科生組成。geatpy是一個高性能實用型進化算法工具箱,提供了許多已實現的進化算法各項操作的函數,如初始化種群、選擇、交叉、變異、多目標優化參考點生成、非支配排序、多目標優化GD、IGD、HV等指標的計算等等。沒有繁雜的深度封裝,可以清晰地看到其基本結構及相關算法的實現,並利用geatpy函數方便地進行自由組合,實現和研究多種改進的進化算法、多目標優化、並行進化算法等,解決傳統優化算法難以解決的問題。
geatpy在Python上提供簡便易用的面向對象的進化算法框架。通過繼承問題類,可以輕松把實際問題與進化算法相結合。geatpy還提供多種進化算法模板類,涵蓋遺傳算法、差分進化算法、進化策略、多目標進化優化算法等,可以通過調用算法模板,輕松解決單目標優化、多目標優化、組合優化、約束優化等問題。這些都是開源的,你可以參照這些模板,加以改進或重構,以便實現效果更好的進化算法,或是讓進化算法更好地與當前正在進行的項目進行融合。
4.1.3 旅行商問題求解
import numpy as np import geatpy as ea class MyProblem(ea.Problem): # 繼承Problem父類 def __init__(self): name = 'MyProblem' # 初始化name(函數名稱,可以隨意設置) M = 1 # 初始化M(目標維數) maxormins = [1] # 初始化maxormins(目標最小最大化標記列表,1:最小化該目標;-1:最大化該目標) Dim = 9 # 初始化Dim(決策變量維數) varTypes = [1] * Dim # 初始化varTypes(決策變量的類型,元素為0表示對應的變量是連續的;1表示是離散的) lb = [1] * Dim # 決策變量下界 ub = [9] * Dim # 決策變量上界 lbin = [1] * Dim # 決策變量下邊界(0表示不包含該變量的下邊界,1表示包含) ubin = [1] * Dim # 決策變量上邊界(0表示不包含該變量的上邊界,1表示包含) # 調用父類構造方法完成實例化 ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin) # 新增一個屬性存儲旅行地坐標 self.places = np.array([[0.4, 0.4439], [0.2439, 0.1463], [0.1707, 0.2293], [0.2293, 0.761], [0.5171, 0.9414], [0.8732, 0.6536], [0.6878, 0.5219], [0.8488, 0.3609], [0.6683, 0.2536], [0.6195, 0.2634]]) def aimFunc(self, pop): # 目標函數 x = pop.Phen # 得到決策變量矩陣 X = np.hstack([np.zeros((x.shape[0], 1)), x, np.zeros((x.shape[0], 1))]).astype(int) # 添加從0地出發且最后回到出發地 ObjV = [] # 存儲所有種群個體對應的總路程 for i in range(X.shape[0]): journey = self.places[X[i], :] # 按既定順序到達的地點坐標 distance = np.sum(np.sqrt(np.sum(np.diff(journey.T) ** 2, 0))) # 計算總路程 ObjV.append(distance) pop.ObjV = np.array([ObjV]).T # 把求得的目標函數值賦值給種群pop的ObjV # 找到違反約束條件的個體在種群中的索引,保存在向量exIdx中(如:若0、2、4號個體違反約束條件,則編程找出他們來) exIdx1 = np.where(np.where(x == 3)[1] - np.where(x == 6)[1] < 0)[0] exIdx2 = np.where(np.where(x == 4)[1] - np.where(x == 5)[1] < 0)[0] exIdx = np.unique(np.hstack([exIdx1, exIdx2])) pop.CV = np.zeros((pop.sizes, 1)) pop.CV[exIdx] = 1 # 把求得的違反約束程度矩陣賦值給種群pop的CV import matplotlib.pyplot as plt if __name__ == '__main__': """================================實例化問題對象============================""" problem = MyProblem() # 生成問題對象 """==================================種群設置==============================""" Encoding = 'P' # 編碼方式,采用排列編碼 NIND = 50 # 種群規模 Field = ea.crtfld(Encoding, problem.varTypes, problem.ranges, problem.borders) # 創建區域描述器 population = ea.Population(Encoding, Field, NIND) # 實例化種群對象(此時種群還沒被初始化,僅僅是完成種群對象的實例化) """================================算法參數設置=============================""" myAlgorithm = ea.soea_SEGA_templet(problem, population) # 實例化一個算法模板對象 myAlgorithm.MAXGEN = 200 # 最大進化代數 myAlgorithm.mutOper.Pm = 0.5 # 變異概率 myAlgorithm.logTras = 1 # 設置每隔多少代記錄日志,若設置成0則表示不記錄日志 myAlgorithm.verbose = True # 設置是否打印輸出日志信息 myAlgorithm.drawing = 1 # 設置繪圖方式(0:不繪圖;1:繪制結果圖;2:繪制目標空間過程動畫;3:繪制決策空間過程動畫) """===========================調用算法模板進行種群進化========================""" [BestIndi, population] = myAlgorithm.run() # 執行算法模板,得到最優個體以及最后一代種群 BestIndi.save() # 把最優個體的信息保存到文件中 """==================================輸出結果==============================""" print('評價次數:%s' % myAlgorithm.evalsNum) print('時間已過 %s 秒' % myAlgorithm.passTime) if BestIndi.sizes != 0: print('最短路程為:%s' % BestIndi.ObjV[0][0]) print('最佳路線為:') best_journey = np.hstack([0, BestIndi.Phen[0, :], 0]) for i in range(len(best_journey)): print(int(best_journey[i]), end=' ') # 繪圖 plt.figure() plt.plot(problem.places[best_journey.astype(int), 0], problem.places[best_journey.astype(int), 1], c='black') plt.plot(problem.places[best_journey.astype(int), 0], problem.places[best_journey.astype(int), 1], 'o',c='black') for i in range(len(best_journey)): plt.text(problem.places[int(best_journey[i]), 0], problem.places[int(best_journey[i]), 1], chr(int(best_journey[i]) + 65), fontsize=20) plt.grid(True) plt.title('連線圖') plt.xlabel('經度') plt.ylabel('經度') plt.savefig('roadmap.svg', dpi=600, bbox_inches='tight') else: print('沒找到可行解。')
4.2 scikit-opt
4.2.1 開源地址
https://github.com/guofei9987/scikit-opt/
4.2.2 簡介
是一款個人維護,容易上手,github上引用多,文檔齊全,接近Python原生語法的智能優化群分算法
4.2.3 旅行商問題求解
import numpy as np from scipy import spatial import matplotlib.pyplot as plt font = {'family':'SimHei','weight':'bold','size':'9'} plt.rc('font',**font) plt.rc('axes',unicode_minus = False) #解決負號顯示問題 num_points = 10 def task_distance(num_points,minValue, maxValue): li = np.array([[7,21,11,28,3,23,28,3,19,23], [11,3,2,17,12,30,21,18,25,5], [6,28,24,11,27,27,7,13,25,10], [26,21,12,11,16,17,18,14,25,19], [7,9,8,18,25,17,23,26,21,27], [30,11,26,2,13,8,3,20,5,25], [8,24,10,10,5,26,5,27,3,16], [28,23,28,11,22,2,15,11,13,16], [17,10,30,3,7,22,23,30,4,2], [16,29,10,28,8,10,2,8,10,2]]) return li def cal_total_distance(routine): num_points, = routine.shape sum =0; for i in range(num_points-1): sum += distance_matrix[routine[i],routine[i+1]] return sum from sko.GA import GA_TSP points_coordinate = task_distance(num_points,2,30) # generate coordinate of points distance_matrix = points_coordinate ga_tsp = GA_TSP(func=cal_total_distance, n_dim=num_points, size_pop=50, max_iter=20, prob_mut=1) best_points, best_distance = ga_tsp.run() print("best_points is ",best_points) print("best_distance is ", best_distance) #畫圖 best_points_ = best_points best_points_coordinate = points_coordinate[best_points_, :] plt.figure(1) plt.subplot(221,title='連線圖') plt.xlabel("經度") plt.ylabel("緯度") plt.plot(best_points_coordinate[:, 0], best_points_coordinate[:, 1], 'o-r') plt.subplot(222,title='收斂圖') plt.xlabel("迭代次數") plt.ylabel("最短距離") plt.plot(ga_tsp.generation_best_Y) plt.show()
4.3 旅行商問題開源框架缺陷
分析2種算法框架,有運行速度快,准確度高等特點,單旅行商無約束模型這2個框架都非常適合。
缺點也很明顯,實際應用中,有順序約束,也有幾個車間任務,幾個巡檢任務同時調度,即多旅行商有約束的情況,這些框架算法就很難實現,甚至不支持,需要自行編碼實現多旅行商有約束的遺傳算法
5 自行編碼實現任務調度
根據遺傳算法原理,自行編碼實現遺傳算法,實現任務調度
5.1 旅行商類
import pandas as pd
import numpy as np import matplotlib.pyplot as plt font = {'family':'SimHei','weight':'bold','size':'9'} plt.rc('font',**font) plt.rc('axes',unicode_minus = False) #解決負號顯示問題 class TPS(object): pop_size = 50 #種群大小 c_rate = 0.7 #交叉率 m_rate = 0.05 #突變率 pop = np.array([])#種群數量 fitness = np.array([]) #適應度數組 ga_num = 200 #最大迭代次數 best_distance = 1 #記錄目前最優距離 best_gene = [] #記錄目前最優任務調度方案 tasks = np.array([]) task_size = 0 #標記任務數目 tasks_distance = np.array([[[]]]) task_distance_start = 1 #任務開始位置 task_cons_order = np.array([[]])#順序約束 task_cons_no = np.array([])
def __init__(self,c_rate,m_rate,pop_size,ga_num,start_pos,task_list,task_cons_order,tasks_cons_number): self.fitness = np.zeros(self.pop_size) self.c_rate = c_rate self.m_rate = m_rate self.pop_size = pop_size self.ga_num = ga_num self.task_distance_start = start_pos self.tasks_distance = np.array(task_list) self.task_size = self.tasks_distance.shape[1] self.tasks = np.arange(self.task_size) self.task_cons_order = np.array(task_cons_order) self.task_cons_no = np.array(tasks_cons_number) def init(self): self.pop = self.creat_pop(self.pop_size) self.fitness = self.get_fitness(self.pop) #計算種群適應度 def creat_pop(self,size): pop = [] for i in range(size): gene = self.creat_gene() #創建基因 pop.append(gene) #加入種群 return np.array(pop) def creat_gene(self,cons_order = True): gene = np.arange(self.task_size) # 問題的解,基因組合,種群中的個體 np.random.shuffle(gene) #打亂基因組合 if cons_order: gene = self.constraint_order(gene) #順序約束 return gene def constraint_order(self,gene):#順序約束 if len(self.task_cons_order)==0 or len(self.task_cons_order[0])==0:#無約束直接返回 return gene for i,elem_one in enumerate(gene):#選擇排序 if gene[i] not in self.task_cons_no: continue for j,elem_two in enumerate(gene): if i==j: continue chage = self.change_order(gene[i],gene[j])#判斷是否要調整順序 if chage: gene[i],gene[j] = gene[j],gene[i] return gene def change_order(self,begin,end):#交換順序 result,cycle = get_link_next(self.task_cons_order,begin,end) return result def get_fitness(self,pop): d = np.array([]) #適應度記錄數組 for i in range(pop.shape[0]): gene = pop[i] #取個體 distance = self.get_distance(gene)#計算個體基因的優劣 distance = self.best_distance/distance #當前最優距離除以個體距離,越近適應度越高,最優適應度為1 d= np.append(d,distance) #保存種群中每個個體適應度 return d def get_distance(self,gene): s = 0 for i in range(len(gene)): if i==0: start_task = self.tasks_distance[0][gene[i]] start_distance_one = self.task_distance_start - start_task[0] start_distance_two = start_task[0]-start_task[1] s+= np.abs(start_distance_one) + np.abs(start_distance_two) else: s+= self.caculate_distance(gene[i-1],gene[i]) return s def caculate_distance(self,i,j): distance_one = self.tasks_distance[0][i][1]-self.tasks_distance[0][j][0] distance_two = self.tasks_distance[0][j][0]-self.tasks_distance[0][j][1] distance = np.abs(distance_one) + np.abs(distance_two) return distance def select_pop(self,pop): #低於平均的要替換改變 best_f_index = np.argmax(self.fitness) av = np.median(self.fitness, axis = 0) for i in range(self.pop_size): if i!=best_f_index and self.fitness[i] < av: pi = self.cross(pop[best_f_index],pop[i]) pi = self.mutate(pi) pop[i,:] = pi[:] return pop;
def cross(self,parent1,parent2):#交叉 if np.random.rand()>self.c_rate: return parent1 index1 = np.random.randint(0,self.task_size-1) index2 = np.random.randint(index1,self.task_size-1) tempGene = parent2[index1:index2] #交叉基因片段 newGene = [] pllen =0 for g in parent1: if pllen == index1: newGene.extend(tempGene) #插入基因片段 if g not in tempGene: newGene.append(g) pllen+=1 newGene = np.array(newGene) if newGene.shape[0]!=self.task_size: print('c error') return self.creat_pop(1) #newGene = self.constraint_order(newGene) #順序約束 return newGene def mutate(self,gene):#變異 if np.random.rand()> self.m_rate: gene = self.constraint_order(gene) #順序約束 return gene index1 = np.random.randint(0,self.task_size-1) index2 = np.random.randint(index1,self.task_size-1) newGene = self.reverse_gene(gene,index1,index2) if newGene.shape[0]!=self.task_size: print('m error') return self.creat_pop(1) newGene = self.constraint_order(newGene) #順序約束 return newGene def reverse_gene(self,gene,i,j):#反轉 #翻轉基因中i到j之間的基因片段 if i>=j: return gene if j>self.task_size-1: return gene parent1 = np.copy(gene) tempGene = parent1[i:j] newGene = [] pllen = 0 for g in parent1: if pllen==i: newGene.extend(tempGene[::-1]) #插入基因片段 if g not in tempGene: newGene.append(g) pllen+=1 return np.array(newGene) def evelution(self): #主程序:迭代進化種群 best_iteration = 0; best_gene = [] best_distance =0 tsp = self x_axis = [] y_axis = [] for i in range(self.ga_num): best_f_index = np.argmax(tsp.fitness) worst_f_index = np.argmin(tsp.fitness) local_best_gene = tsp.pop[best_f_index] local_best_distance = tsp.get_distance(local_best_gene) if i ==0: tsp.best_gene = local_best_gene tsp.best_distance = tsp.get_distance(local_best_gene) best_gene = tsp.best_gene best_distance = tsp.best_distance if local_best_distance < best_distance: best_iteration = i tsp.best_distance = local_best_distance #記錄最優值 tsp.best_gene = local_best_gene #記錄最優個體基因 best_gene = tsp.best_gene best_distance = tsp.best_distance else: tsp.pop[worst_f_index] = tsp.cross(self.creat_gene(cons_order = False),best_gene) tsp.pop[worst_f_index] = tsp.mutate(tsp.pop[worst_f_index]) this_best_distance = self.get_distance(self.best_gene) x_axis.append(i+1) y_axis.append(this_best_distance) print('當前迭代:%d 全局最優值:%s 本次最優值:%s 本次最優染色體:%s'%(i+1,best_distance,this_best_distance,self.best_gene)) tsp.pop = tsp.select_pop(tsp.pop) #選擇淘汰種群 #創建新種群 best_gene_number = 0 #保留一條最優解 for j in range(self.pop_size): best_gene_equal = (tsp.pop[j] == best_gene).all() if best_gene_equal: best_gene_number+=1 #print("第%d次迭代包含相等的最優距離,個數%d"%(i+1,best_gene_number)) if best_gene_equal==False or best_gene_number>1: r = np.random.randint(0,self.pop_size-1) if j!=r: tsp.pop[j] == tsp.cross(tsp.pop[j],tsp.pop[r]) #交叉種群中第j,r個體的基因 tsp.pop[j] = tsp.mutate(tsp.pop[j]) #突變種群中第j個個體基因 self.fitness = self.get_fitness(self.pop) #計算種群適應度
plt.xlabel("迭代次數") plt.ylabel("最短距離") plt.title("收斂圖(第%d次迭代出現最短距離%d)"%(best_iteration+1,best_distance)) plt.plot(x_axis,y_axis) plt.show() print('最優迭代:%d 最優值:%s 最優染色體:%s'%(best_iteration+1,best_distance,best_gene)) best_gene_str = self.get_gene_str(best_gene) result = "%d|%s"%(best_distance,best_gene_str) print(result) return result def get_gene_str(self,gene): genStr = '' for i in gene: genStr += str(i)+"," return genStr[0:-1]
5.2 輔助函數
def get_task_into(t): #轉為任務列表 #t=1,1|2,2|3,3|4,4|5,5|6,6|7,7|8,8|9,9|10,10 task = [] #任務 task_list = [] #任務列表 cons_order_list = [] #順序約束列表 task_cons_no = [] for i,elem_one in enumerate(t.split("|")): task_bay = []#貝位:開始貝位,結束貝位 cons_order =[] #順序約束 for j,elem_two in enumerate(elem_one.split(",")): if j<2: task_bay.append(int(elem_two)) elif j==2 and elem_two!='': cons_order.append(i) end = int(elem_two) cons_order.append(end) cons_order_list.append(cons_order) if i not in task_cons_no: task_cons_no.append(i) if end not in task_cons_no: task_cons_no.append(end) task.append(task_bay) task_list.append(task) return task_list,cons_order_list,task_cons_no def dead_cycle(arr):#死循環判斷 arr = np.array([[5,1],[1,3],[3,5]]) for i in range(len(arr)): if len(arr[i])==0: continue begin,end = arr[i][0],arr[i][1] result,cycle = get_link_next(arr,begin,end) if result: print("發生死循環任務編號%d"%(cycle)) return result return False def get_link_next(arr,begin,end):#獲取鏈路下一個,返回是否死循環和最后的 for j in range(len(arr)): if len(arr[j])==0 or (begin == arr[j][0] and end == arr[j][1]): #排除本身 continue if end == arr[j][0]: #向后 if begin == arr[j][1]: #print("發生死循環任務指針編號%d,%d"%(begin,end)) return True,end else: end = arr[j][1] result = get_link_next(arr,begin,end) return result return False,end
5.3 調用主方法
if __name__ == '__main__': #app.run(debug=True, port=8888,host=0.0.0.0) #指定端口、host,0.0.0.0代表不管幾個網卡,任何ip都可以訪問 start_pos = int(1) task_list,cons_order,task_cons_no = get_task_into('1,1|2,2,3,|3,3|4,4|5,5|6,6|7,7|8,8|9,9|10,10|1,1|2,2|3,3|4,4|5,5') dead = dead_cycle(np.array(cons_order)) #驗證死循環 if dead: print("發生死循環了") else: iteration = len(task_cons_no) print("順序約束任務個數數為%d"%(iteration)) iteration = 16-len(task_cons_no) if iteration <=0: iteration =1 self = TPS(0.8,0.05,2000,iteration,start_pos,task_list,cons_order,task_cons_no) self.init() result = self.evelution()
5.4 WISG run_server部署方法
#導入WISG(Web Server Gateway Interface) from wsgiref.simple_server import make_server from urllib import parse import json import pdb
def getSingleTaskSchedule(environ,start_response):
start_response('200 OK',[('Content-Type','text/html;charset=urf-8')]) #調用urlparse的parse_qs解析URL參數,並返回字典 query_args=environ['QUERY_STRING'] params = parse.parse_qs(query_args) start_pos_value = params['startPos'] task_list_value = params['taskList'] # 獲取get中key為name的值 start_pos = int(str(start_pos_value).replace("[","").replace("]","").replace("'","")) task_list,cons_order,tasks_cons_number = get_task_into(str(task_list_value).replace("[","").replace("]","").replace("'","")) dead = dead_cycle(np.array(cons_order)) #驗證死循環 if dead: result = json.dumps("-9998|任務死循環").encode('utf-8') return [result] else: iteration = len(tasks_cons_number) print("順序約束任務個數數為%d"%(iteration)) iteration = 16-len(tasks_cons_number) if iteration <=0: iteration =1 self = TPS(0.7,0.05,2000,iteration,start_pos,task_list,cons_order,tasks_cons_number) self.init() result = json.dumps(self.evelution()).encode('utf-8') return [result] return [result] def get_url(): data= {'/getSingleTaskSchedule':getSingleCraneSchedule}
return data def run_server(environ,start_response): urls=get_url() url=environ.get("PATH_INFO") print(url) if url in urls: function_data = urls[url](environ,start_response) return function_data else: start_response("200 OK",[('Content-Type','text/html;charset=utf-8')]) result = [bytes("-9999|路由錯誤",encoding="utf-8")] return result httpd =make_server('0.0.0.0',8801,run_server) # ip='0.0.0.0' port=8801 print("server is started, port is 8801....") httpd.serve_forever()
5.5 運行結果
順序約束任務個數數為2 當前迭代:1 全局最優值:28 本次最優值:28 本次最優染色體:[14 4 9 8 7 6 2 3 12 13 5 11 10 1 0] 當前迭代:2 全局最優值:24 本次最優值:24 本次最優染色體:[14 4 9 8 7 6 5 2 3 12 13 11 10 1 0] 當前迭代:3 全局最優值:20 本次最優值:20 本次最優染色體:[14 4 9 8 7 6 5 3 13 2 12 11 10 1 0] 當前迭代:4 全局最優值:18 本次最優值:18 本次最優染色體:[14 4 9 8 7 6 5 3 13 2 12 11 1 10 0] 當前迭代:5 全局最優值:18 本次最優值:18 本次最優染色體:[14 4 9 8 7 6 5 3 13 2 12 11 1 10 0] 當前迭代:6 全局最優值:18 本次最優值:18 本次最優染色體:[14 4 9 8 7 6 5 3 13 2 12 11 1 10 0] 當前迭代:7 全局最優值:18 本次最優值:18 本次最優染色體:[14 4 9 8 7 6 5 3 13 2 12 11 1 10 0] 當前迭代:8 全局最優值:18 本次最優值:18 本次最優染色體:[14 4 9 8 7 6 5 3 13 2 12 11 1 10 0] 當前迭代:9 全局最優值:18 本次最優值:18 本次最優染色體:[14 4 9 8 7 6 5 3 13 2 12 11 1 10 0] 當前迭代:10 全局最優值:18 本次最優值:18 本次最優染色體:[14 4 9 8 7 6 5 3 13 2 12 11 1 10 0] 當前迭代:11 全局最優值:18 本次最優值:18 本次最優染色體:[14 4 9 8 7 6 5 3 13 2 12 11 1 10 0] 當前迭代:12 全局最優值:18 本次最優值:18 本次最優染色體:[14 4 9 8 7 6 5 3 13 2 12 11 1 10 0] 當前迭代:13 全局最優值:18 本次最優值:18 本次最優染色體:[14 4 9 8 7 6 5 3 13 2 12 11 1 10 0] 當前迭代:14 全局最優值:18 本次最優值:18 本次最優染色體:[14 4 9 8 7 6 5 3 13 2 12 11 1 10 0]
最優迭代:4 最優值:18 最優染色體:[14 4 9 8 7 6 5 3 13 2 12 11 1 10 0]
18|14,4,9,8,7,6,5,3,13,2,12,11,1,10,0