2 _5 w. S9 H5 t' [' T有了解码得到路径,如何求个体的适应度? 9 Z6 k* d6 c. K# d6 N, `# s) X % C2 e; ]# _& M* d" z想要求个体的适应度,首先要定一个优化目标。本题是要让路径最短,于是我们便要根据访问结点求出路径长度,把这个作为优化目标。有了优化目标,便可利用基于目标函数值排序的适应度分配(ranking)求出适应度值。当然,这类单目标优化问题也可以直接让个体的适应度等于优化目标函数值(即路径长度)。而路径长度即为访问路径上各条有向边的权值之和。: A4 W. |5 d- H1 E. e, H
! c. X$ H* w* j7 g7 \+ e/ g a
代码实现: 7 m) [+ B( F# I- v$ D7 n* f + h- g2 J6 r. [; S1 }9 @首先编写问题类,把待优化模型写在问题类中。然后编写执行脚本,调用“soea_SEGA_templet“(增强精英保留的遗传算法模板)进行进化优化。该算法模板的源码及算法流程详见:/ m0 G% k& h5 W/ [5 p
/ A, T* |7 ~! Chttps://github.com/geatpy-dev/geatpy/blob/master/geatpy/templates/soeas/GA/SEGA/soea_SEGA_templet.py 6 [) K8 X4 \2 D7 H % c" o6 R, `, j- R: j B由于本题比较简单,故用4个个体、10代的进化即可得到很好的结果。完整的实验代码如下:" G, L. r0 _0 Q& k6 N% u
# F6 ?- `6 p: m- ^1 D
# -*- coding: utf-8 -*-) t# B/ C1 c7 n8 V! s: [# b% C
import numpy as np: t$ R9 O, R, |% W
import geatpy as ea ' K5 O5 s( ^; Y+ F& }class MyProblem(ea.Problem): # 继承Problem父类0 c" q, @ p3 n, ^/ J6 a
def __init__(self):0 P, ^, Z- [& c% r: n) w
name = 'Shortest_Path' # 初始化name(函数名称,可以随意设置) 6 [/ H! h7 u/ {- h; A* Z M = 1 # 初始化M(目标维数) 8 }6 W6 U% o6 y2 m: W maxormins = [1] # 初始化maxormins(目标最小最大化标记列表,1:最小化该目标;-1:最大化该目标)1 p$ D4 _9 o! z9 z& i
Dim = 10 # 初始化Dim(决策变量维数) % q2 {8 c4 P0 L+ O2 D6 U varTypes = [1] * Dim # 初始化varTypes(决策变量的类型,元素为0表示对应的变量是连续的;1表示是离散的)5 Y# `# O% k1 p( T! t! W
lb = [0] * Dim # 决策变量下界 : H+ v6 G8 b4 f: m4 b4 R ub = [9] * Dim # 决策变量上界2 r, v1 |+ c% o9 ^& ?3 k( x, `
lbin = [1] * Dim # 决策变量下边界1 d" V, g; N9 v7 ?0 B) }' r( b
ubin = [1] * Dim # 决策变量上边界 ( @7 ^4 Z: J% C; x/ O U S # 调用父类构造方法完成实例化 2 Y* t2 e u/ ~4 ]0 N' m2 | ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)7 q/ D2 |% X/ i/ `/ X& c$ @
# 设置每一个结点下一步可达的结点(结点从1开始数,因此列表nodes的第0号元素设为空列表表示无意义)2 f- z9 q1 f6 k" j
self.nodes = [[], [2,3], [3,4,5], [5,6], [7,8], [4,6], [7,9], [8,9], [9,10], [10]]) D$ w. G8 }# u- k/ ~4 ]
# 设置有向图中各条边的权重 ( e% e, _: Z5 m4 M9 S( D- } self.weights = {'(1, 2)':36, '(1, 3)':27, '(2, 4)':18, '(2, 5)':20, '(2, 3)':13, '(3, 5)':12, '(3, 6)':23, * ^6 q& e8 d' \ '(4, 7)':11, '(4, 8)':32, '(5, 4)':16, '(5, 6)':30, '(6, 7)':12, '(6, 9)':38, '(7, 8)':20,1 t' L' k! w4 B
'(7, 9)':32, '(8, 9)':15, '(8, 10)':24, '(9, 10)':13} 2 \; j; d: _9 }; C* Q" ]8 D+ L( o* k def decode(self, priority): # 将优先级编码的染色体解码得到一条从节点1到节点10的可行路径 4 x: B: a; c8 j8 h6 a edges = [] # 存储边7 W7 o" C2 d8 D) j6 l$ d. x# z
path = [1] # 结点1是路径起点 ( k$ j3 G+ T' L4 d while not path[-1] == 10: # 开始从起点走到终点) S9 y9 r: t; n: ?, j8 N) m
currentNode = path[-1] # 得到当前所在的结点编号" y9 k/ ~) E" s6 n8 j' t( n: Z
nextNodes = self.nodes[currentNode] # 获取下一步可达的结点组成的列表 ; v. z: X' c2 j7 }! O( e8 M; { if len(nextNodes) == 0: , V' @- @; c! c+ i raise RuntimeError("Error in decode: No way to go. (当前结点出度为0,无路可走。)") v/ O9 P/ F& h4 U8 r chooseNode = nextNodes[np.argmax(priority[np.array(nextNodes) - 1])] # 从NextNodes中选择优先级更高的结点作为下一步要访问的结点,因为结点从1数起,而下标从0数起,因此要减去1 5 L" d" p! \/ e B4 ^1 [ path.append(chooseNode). m Q) U) ?& g/ X! ~4 _9 {) X3 F
edges.append((currentNode, chooseNode)) Y) ?5 b( y) B: ~2 A, Q
return path, edges " H3 C2 T) M5 ~* |: ]6 @ def aimFunc(self, pop): # 目标函数 6 e; m* s$ q, _0 V pop.ObjV = np.zeros((pop.sizes, 1)) # 初始化ObjV ! W' `9 m# N& V* r* ?' t, [3 M, @) _ for i in range(pop.sizes):; \% }7 x: R! r) U; B* f) i, w
priority = pop.Phen[i, :]% D/ P$ b5 t f* B K8 w& B
path, edges = self.decode(priority) # 将优先级编码的染色体解码得到访问路径及经过的边 7 z( j! y& t7 B" B0 I, P pathLen = 0* P# v7 j' }4 ~2 H
for edge in edges:( d& g8 x, F1 N2 e6 q: _; d
key = str(edge) # 根据路径得到键值,以便根据键值找到路径对应的长度, ]8 w6 u {4 h* B+ c" z
if not key in self.weights: # z! E7 @+ `' h$ {$ @ raise RuntimeError("Error in aimFunc: The path is invalid. (当前路径是无效的。)", path) 7 @; Z$ G9 d6 [# o* C" C pathLen += self.weights[key] # 将该段路径长度加入 6 v" [- t% d; D- W8 X! m pop.ObjV = pathLen # 计算目标函数值,赋值给pop种群对象的ObjV属性 9 \8 z2 S- x& Q$ B' ~$ T: K' I, z/ }* }
if __name__ == "__main__": + y' a+ \5 T9 t& d; p, e problem = MyProblem() # 生成问题对象' `5 F/ m4 X2 V1 w' p) |; L* H
"""=================================种群设置================================="""3 x+ L6 M7 J% N5 {) a
Encoding = 'P' # 编码方式* @ _: i, r4 r. ~
NIND = 4 # 种群规模9 p2 D" u. `4 G' J. o2 ]
Field = ea.crtfld(Encoding, problem.varTypes, problem.ranges, problem.borders) # 创建区域描述器 , y7 ] w, Z$ ?) G$ b2 @6 _: j population = ea.Population(Encoding, Field, NIND) # 实例化种群对象(此时种群还没被初始化,仅仅是完成种群对象的实例化)/ _ I: o1 y$ T7 m: }' Q
"""===============================算法参数设置===============================""" ( Q" B8 M/ K' j; q myAlgorithm = ea.soea_SEGA_templet(problem, population) # 实例化一个算法模板对象9 ]; i0 g: v# p
myAlgorithm.MAXGEN = 10 # 最大进化代数2 w, x( m* ]9 |0 `+ Z/ V
myAlgorithm.recOper = ea.Xovox(XOVR = 0.8) # 设置交叉算子+ V2 Y4 ^, U" S: j+ `1 P
myAlgorithm.mutOper = ea.Mutinv(Pm = 0.1) # 设置变异算子 0 t7 }$ s& g& j myAlgorithm.drawing = 1 # 设置绘图方式(0:不绘图;1:绘制结果图;2:绘制过程动画) v s C( k4 N' F
"""==========================调用算法模板进行种群进化=========================="""5 I5 f- Q3 S/ J/ @, H
[population, obj_trace, var_trace] = myAlgorithm.run() # 执行算法模板,得到最后一代种群以及进化记录器 % L6 d/ o1 i8 N, _3 R population.save() # 把最后一代种群的信息保存到文件中 ) C* m. D8 }: g4 h1 B: H! w4 W. l # 输出结果) P2 J% A: T( _! |
best_gen = np.argmin(obj_trace[:, 1]) # 记录最优种群是在哪一代 3 x' ~ R' p2 g( c3 P, R W3 t/ r best_ObjV = np.min(obj_trace[:, 1])8 y3 i) U2 l3 O) A. b6 Z0 |
print('最短路程为:%s'%(best_ObjV))* `1 t$ q, S0 M: _. ^
print('最佳路线为:')( V" o& q: l1 B- a$ v) K' x
best_journey, edges = problem.decode(var_trace[best_gen, :]) 2 q$ u$ N+ D6 h- j) } for i in range(len(best_journey)):9 S& O; e% G- a; M4 ^/ ~, e
print(int(best_journey), end = ' ') 1 K- E; ^2 R# x" { print() $ O% y) {6 }3 g1 P print('有效进化代数:%s'%(obj_trace.shape[0])), e6 F6 w" _3 c2 d- I
print('最优的一代是第 %s 代'%(best_gen + 1)) 7 U. O% \5 ^5 G0 k+ J& E! `& B3 H4 q% O print('评价次数:%s'%(myAlgorithm.evalsNum)) 5 [* q+ d# g# q4 J, _ b! q4 e print('时间已过 %s 秒'%(myAlgorithm.passTime))8 {1 k8 v9 e/ K6 e% ?6 n, ^ Q
在有向图中表现为:* P* n, f7 r6 Y9 m" u8 t