文章目录
1 遗传算法总体设计2 算子设计2.1 选择算子2.2 交叉算子2.3 变异算子2.4 将种群的个体按照里程排序,并返回当前种群中的最优个体及其里程2.5 设置种群数,变异概率及其进化次数2.6 主函数
3 实验结果分析4 附python源码(完整版)
1 遗传算法总体设计
Step1: 初始化参数(种群数、进化次数、变异概率,此次实验并未用到交叉概率,交叉由保留的父代随机进行);
Step2: 初始种群由贪婪算法求得的个体以及其他随机生成的个体构成,评价每一个个体的适配值(路径里程决定);
Step3: 判断算法的收敛准则是否满足(此处为迭代次数)。若满足收敛准则,则输出搜索结果,否则执行Step(4~8);
Step4: 执行选择操作根据个体适配值随机选择作为父代;
Step5: 父代两两个体随机进行交叉操作生成子代;
Step6: 父代变异,子代按概率变异,只有变异后的个体优于变异前的个体才接受该个体变异;
Step7: 更新种群个体(变异后的父代+子代)对应的适配值,更新当前最优解;
Step8: 返回Step3 判断是否进行下一次迭代;
注:以下算法需要用到的数据均和禁忌搜索算法中的处理一致,因此这里不再阐述。
2 算子设计
2.1 选择算子
将种群个体按照里程由小到大排列,排列后的种群个体的适应度:
f
i
t
n
e
s
s
=
1
−
位
次
种
群
规
模
fitness = 1-\frac{位次}{种群规模}
fitness=1−种群规模位次。
采用random库中的random方法,随机生成0~1·之间的数p,若p<fitness则将该个体作为父代保留。
代码实现
def selection(population
):
'''
选出父代个体
'''
M
= population_size
parents
= []
for i
in range(M
):
if random
.random
() < (1 - i
/M
):
parents
.append
(population
[i
])
return parents
2.2 交叉算子
POSITION BASED CROSSOVER(PX)与CYCLE CROSSOVER(CX)的混合。
POSITION BASED CROSSOVER
Step1: 从parent1中随机选择N个点
Step2: 在child1中的相同位置复制这N个点,其余位置为空。
Step3:将在parent2但未在child1中的点按照在parent2中的相对顺序填充到child1中的空位中。
Step4: parent1与parent2交换角色,重复以上步骤,得出child2。
例:路径parent1=【1、2、3、4、5、6、7、8、9】,parent2=【5、4、6、3、1、9、2、7、8】
从parent1中随机选取4个点如【2、5、6、9】
parent1:
复制选中的点到child1:
parent2:
将在parent2但未在child1中的点【4、3、1、7、6】按照在parent2中的相对顺序填充到child1中的空位中。
CYCLE CROSSOVER
CX交叉方法与PX交叉方法的主要不同点就是第一步选点方式的不同,因此这里只介绍CX的选点方式,交叉方式同PX。
Step1: P1中的第一个点为起点start,P2中的第一个点为end,将start添加到cross_points中。
Step2:找寻P1中end的位置position,令P2中position对应的点为end
Step3:将end添加到cross_points中
Step4:重复step2,3直到end=start
交叉算法流程图:
代码实现
def CPX(parent1
,parent2
):
'''
交叉繁殖:CX与PX的混合双亲产生两个子代
'''
cycle
= []
start
= parent1
[0]
cycle
.append
(start
)
end
= parent2
[0]
while end
!= start
:
cycle
.append
(end
)
end
= parent2
[parent1
.index
(end
)]
child
= parent1
[:]
cross_points
= cycle
[:]
if len(cross_points
) < 2 :
cross_points
= random
.sample
(parent1
,2)
k
= 0
for i
in range(len(parent1
)):
if child
[i
] in cross_points
:
continue
else:
for j
in range(k
,len(parent2
)):
if parent2
[j
] in cross_points
:
continue
else:
child
[i
] = parent2
[j
]
k
= j
+ 1
break
return child
2.3 变异算子
采用inversion方法:随机选择两个索引为变异位点,将两个位点之间的数据倒置。
如:child = 【1、2、3、4、5、6、7、8、9】
选择变异位点为【2,5】
变异后:
算法思想:对子代的每一个个体进行概率性变异。若生成的随机数小于设定的变异率,则进行变异。若变异后的个体劣于变异前的个体,则拒绝该个体的变异,保持原状(接受法则)。
代码实现:
def mutation(children
,mutation_rate
):
'''
子代变异
'''
for i
in range(len(children
)):
if random
.random
() < mutation_rate
:
child
= children
[i
]
new_child
= child
[:]
index
= sorted(random
.sample
(indexs
,2))
L
= index
[1] - index
[0] + 1
for j
in range(L
):
new_child
[index
[0]+j
] = child
[index
[1]-j
]
path
= [origin
] + child
+ [origin
]
a
,b
= index
[0] + 1,index
[1] + 1
d1
= dis
[path
[a
-1]-1][path
[a
]-1] + dis
[path
[b
]-1][path
[b
+1]-1]
d2
= dis
[path
[a
-1]-1][path
[b
]-1] + dis
[path
[a
]-1][path
[b
+1]-1]
if d2
< d1
:
children
[i
] = new_child
return children
此外,在程序中父代将进行整体变异(mutation_rate=1),变异仍然遵守接受法则。
2.4 将种群的个体按照里程排序,并返回当前种群中的最优个体及其里程
def get_best_current(population
):
'''
将种群的个体按照里程排序,并返回当前种群中的最优个体及其里程
'''
graded
= [[route_mile_cost
(x
),x
] for x
in population
]
graded
= sorted(graded
)
population
= [x
[1] for x
in graded
]
return graded
[0][0],graded
[0][1],population
2.5 设置种群数,变异概率及其进化次数
population_size
= 100
mutation_rate
= 0.2
iter_count
= 1000
2.6 主函数
流程图
代码实现
def main(iter_count
):
population
= [greedy_initial_route
(remain_cities
)]
for i
in range(population_size
-1):
individual
= remain_cities
[:]
random
.shuffle
(individual
)
population
.append
(individual
)
mile_cost
,result
,population
= get_best_current
(population
)
record
= [mile_cost
]
i
= 0
while i
< iter_count
:
parents
= selection
(population
)
target_count
= population_size
- len(parents
)
children
= []
while len(children
) < target_count
:
parent1
,parent2
= random
.sample
(parents
,2)
child1
= CPX
(parent1
,parent2
)
child2
= CPX
(parent2
,parent1
)
children
.append
(child1
)
children
.append
(child2
)
parents
= mutation
(parents
,1)
children
= mutation
(children
,mutation_rate
)
population
= parents
+ children
mile_cost
,result
,population
= get_best_current
(population
)
record
.append
(mile_cost
)
i
+= 1
route
= [origin
] + result
+ [origin
]
return route
,mile_cost
,record
绘制进化过程图
def fig():
time_start
= time
.time
()
N
= 1000
satisfactory_solution
,mile_cost
,record
= main
(N
)
time_end
= time
.time
()
time_cost
= time_end
- time_start
print('time cost:',time_cost
)
print("优化里程成本:%d" %(int(mile_cost
)))
print("优化路径:\n",satisfactory_solution
)
X
= []
Y
= []
for i
in satisfactory_solution
:
x
= city_location
[i
-1][0]
y
= city_location
[i
-1][1]
X
.append
(x
)
Y
.append
(y
)
plt
.plot
(X
,Y
,'-o')
plt
.title
("satisfactory solution of TS:%d"%(int(mile_cost
)))
plt
.show
()
A
= [i
for i
in range(N
+1)]
B
= record
[:]
plt
.xlim
(0,N
)
plt
.xlabel
('进化次数',fontproperties
="SimSun")
plt
.ylabel
('路径里程',fontproperties
="SimSun")
plt
.title
("solution of GA changed with evolution")
plt
.plot
(A
,B
,'-')
plt
.show
()
3 实验结果分析
对berlin52,在不同的种群数和进化次数下每组做10次重复实验,得出组内平均gap值以及程序平均运行时间如下:
平均gap值
程序平均运行时间(秒)
结果分析:在进化次数相同的情况下,随着种群数的增加,GA的最终结果往往更趋近于最优解。种群数相同的情况下,进化次数的增加并不一定能够改善GA结果。这意味着在进化后期,算法陷入了局部最优。当然,无论种群数的增加还是进化次数的增加都会导致程序运行时间的增加。
以ch150为例,以下结果为GA运算中得出的ch150算例最好的满意解gap值为1.52%
GA搜索路径结果
GA进化过程
4 附python源码(完整版)
import math
,random
,time
import matplotlib
.pyplot
as plt
filename
= '算例\\berlin52.txt'
city_num
= []
city_location
= []
with open(filename
, 'r') as f
:
datas
= f
.readlines
()[6:-1]
for data
in datas
:
data
= data
.split
()
city_num
.append
(int(data
[0]))
x
= float(data
[1])
y
= float(data
[2])
city_location
.append
((x
,y
))
city_count
= len(city_num
)
origin
= 1
remain_cities
= city_num
[:]
remain_cities
.remove
(origin
)
remain_count
= city_count
- 1
indexs
= list(i
for i
in range(remain_count
))
dis
=[[0]*city_count
for i
in range(city_count
)]
for i
in range(city_count
):
for j
in range(city_count
):
if i
!= j
:
dis
[i
][j
] = math
.sqrt
((city_location
[i
][0]-city_location
[j
][0])**2 + (city_location
[i
][1]-city_location
[j
][1])**2)
else:
dis
[i
][j
] = 0
def route_mile_cost(route
):
'''
计算路径的里程成本
'''
mile_cost
= 0.0
mile_cost
+= dis
[0][route
[origin
-1]-1]
for i
in range(remain_count
-1):
mile_cost
+= dis
[route
[i
]-1][route
[i
+1]-1]
mile_cost
+= dis
[route
[-1]-1][origin
-1]
return mile_cost
def nearest_city(current_city
,remain_cities
):
temp_min
= float('inf')
next_city
= None
for i
in range(len(remain_cities
)):
distance
= dis
[current_city
-1][remain_cities
[i
]-1]
if distance
< temp_min
:
temp_min
= distance
next_city
= remain_cities
[i
]
return next_city
def greedy_initial_route(remain_cities
):
'''
采用贪婪算法生成初始解:从第一个城市出发找寻与其距离最短的城市并标记,
然后继续找寻与第二个城市距离最短的城市并标记,直到所有城市被标记完。
最后回到第一个城市(起点城市)
'''
cand_cities
= remain_cities
[:]
current_city
= origin
initial_route
= []
while len(cand_cities
) > 0:
next_city
= nearest_city
(current_city
,cand_cities
)
initial_route
.append
(next_city
)
current_city
= next_city
cand_cities
.remove
(next_city
)
return initial_route
def selection(population
):
'''
选出父代个体
'''
M
= population_size
parents
= []
for i
in range(M
):
if random
.random
() < (1 - i
/M
):
parents
.append
(population
[i
])
return parents
def CPX(parent1
,parent2
):
'''
交叉繁殖:CX与PX的混合双亲产生两个子代
'''
cycle
= []
start
= parent1
[0]
cycle
.append
(start
)
end
= parent2
[0]
while end
!= start
:
cycle
.append
(end
)
end
= parent2
[parent1
.index
(end
)]
child
= parent1
[:]
cross_points
= cycle
[:]
if len(cross_points
) < 2 :
cross_points
= random
.sample
(parent1
,2)
k
= 0
for i
in range(len(parent1
)):
if child
[i
] in cross_points
:
continue
else:
for j
in range(k
,len(parent2
)):
if parent2
[j
] in cross_points
:
continue
else:
child
[i
] = parent2
[j
]
k
= j
+ 1
break
return child
def mutation(children
,mutation_rate
):
'''
子代变异
'''
for i
in range(len(children
)):
if random
.random
() < mutation_rate
:
child
= children
[i
]
new_child
= child
[:]
index
= sorted(random
.sample
(indexs
,2))
L
= index
[1] - index
[0] + 1
for j
in range(L
):
new_child
[index
[0]+j
] = child
[index
[1]-j
]
path
= [origin
] + child
+ [origin
]
a
,b
= index
[0] + 1,index
[1] + 1
d1
= dis
[path
[a
-1]-1][path
[a
]-1] + dis
[path
[b
]-1][path
[b
+1]-1]
d2
= dis
[path
[a
-1]-1][path
[b
]-1] + dis
[path
[a
]-1][path
[b
+1]-1]
if d2
< d1
:
children
[i
] = new_child
return children
def get_best_current(population
):
'''
将种群的个体按照里程排序,并返回当前种群中的最优个体及其里程
'''
graded
= [[route_mile_cost
(x
),x
] for x
in population
]
graded
= sorted(graded
)
population
= [x
[1] for x
in graded
]
return graded
[0][0],graded
[0][1],population
population_size
= 100
mutation_rate
= 0.2
def main(iter_count
):
population
= [greedy_initial_route
(remain_cities
)]
for i
in range(population_size
-1):
individual
= remain_cities
[:]
random
.shuffle
(individual
)
population
.append
(individual
)
mile_cost
,result
,population
= get_best_current
(population
)
record
= [mile_cost
]
i
= 0
while i
< iter_count
:
parents
= selection
(population
)
target_count
= population_size
- len(parents
)
children
= []
while len(children
) < target_count
:
parent1
,parent2
= random
.sample
(parents
,2)
child1
= CPX
(parent1
,parent2
)
child2
= CPX
(parent2
,parent1
)
children
.append
(child1
)
children
.append
(child2
)
parents
= mutation
(parents
,1)
children
= mutation
(children
,mutation_rate
)
population
= parents
+ children
mile_cost
,result
,population
= get_best_current
(population
)
record
.append
(mile_cost
)
i
+= 1
route
= [origin
] + result
+ [origin
]
return route
,mile_cost
,record
def fig():
time_start
= time
.time
()
N
= 1000
satisfactory_solution
,mile_cost
,record
= main
(N
)
time_end
= time
.time
()
time_cost
= time_end
- time_start
print('time cost:',time_cost
)
print("优化里程成本:%d" %(int(mile_cost
)))
print("优化路径:\n",satisfactory_solution
)
X
= []
Y
= []
for i
in satisfactory_solution
:
x
= city_location
[i
-1][0]
y
= city_location
[i
-1][1]
X
.append
(x
)
Y
.append
(y
)
plt
.plot
(X
,Y
,'-o')
plt
.title
("satisfactory solution of TS:%d"%(int(mile_cost
)))
plt
.show
()
A
= [i
for i
in range(N
+1)]
B
= record
[:]
plt
.xlim
(0,N
)
plt
.xlabel
('进化次数',fontproperties
="SimSun")
plt
.ylabel
('路径里程',fontproperties
="SimSun")
plt
.title
("solution of GA changed with evolution")
plt
.plot
(A
,B
,'-')
plt
.show
()
return mile_cost
,time_cost