参考 https://blog.csdn.net/qq_27755195/article/details/56597467
https://blog.csdn.net/emiyasstar__/article/details/6938608/
应用
本质是一种高效、并行、全局搜索的方法,能在搜索过程中自动获取和积累有关搜索空间的知识,并自适应地(遗传)控制搜索过程以求得最佳解.
如果是凸函数,凹函数,我们可以很快的得出最优解。
但如果是存在很多局部最优解的情况下,再用求n阶导的方法,就不能容易得到结果。
遗传算法的有趣应用很多,诸如寻路问题,8数码问题,囚犯困境,动作控制,找圆心问题(在一个不规则的多边形中,寻找一个包含在该多边形内的最大圆圈的圆心),TSP问题,生产调度问题,人工生命模拟等。下面我以袋鼠为例子讲讲遗传算法。(因为袋鼠会跳)
可以这样想象,这个多维曲面里面有数不清的“山峰”,而这些山峰所对应的就是局部最优解。而其中也会有一个“山峰”的海拔最高的,那么这个就是全局最优解。而遗传算法的任务就是尽量爬到最高峰,而不是陷落在一些小山峰。(另外,值得注意的是遗传算法不一定要找“最高的山峰”,如果问题的适应度评价越小越好的话,那么全局最优解就是函数的最小值,对应的,遗传算法所要找的就是“最深的谷底”)
袋鼠引入
引入“袋鼠跳”,理解下基本的算法流程。
既然我们把函数曲线理解成一个一个山峰和山谷组成的山脉。那么我们可以设想所得到的每一个解就是一只袋鼠,我们希望它们不断的向着更高处跳去,直到跳到最高的山峰(尽管袋鼠本身不见得愿意那么做)。所以求最大值的过程就转化成一个“袋鼠跳”的过程。
对比下面简单介绍“袋鼠跳”的几种方式:
- 爬山法(最速上升爬山法):
从搜索空间中随机产生邻近的点,从中选择对应解最优的个体,替换原来的个体,不断重复上述过程。因为爬山法只对“邻近”的点作比较,所以目光比较“短浅”,常常只能收敛到离开初始位置比较近的局部最优解上面。对于存在很多局部最优点的问题,通过一个简单的迭代找出全局最优解的机会非常渺茫。(在爬山法中,袋鼠最有希望到达最靠近它出发点的山顶,但不能保证该山顶是珠穆朗玛峰,或者是一个非常高的山峰。因为一路上它只顾上坡,没有下坡。)- 模拟退火:
这个方法来自金属热加工过程的启发。在金属热加工过程中,当金属的温度超过它的熔点(Melting Point)时,原子就会激烈地随机运动。与所有的其它的物理系统相类似,原子的这种运动趋向于寻找其能量的极小状态。在这个能量的变迁过程中,开始时,温度非常高, 使得原子具有很高的能量。随着温度不断降低,金属逐渐冷却,金属中的原子的能量就越来越小,最后达到所有可能的最低点。利用模拟退火的时候,让算法从较大的跳跃开始,使到它有足够的“能量”逃离可能“路过”的局部最优解而不至于限制在其中,当它停在全局最优解附近的时候,逐渐的减小跳跃量,以便使其“落脚 ”到全局最优解上。(在模拟退火中,袋鼠喝醉了,而且随机地大跳跃了很长时间。运气好的话,它从一个山峰跳过山谷,到了另外一个更高的山峰上。但最后,它渐渐清醒了并朝着它所在的峰顶跳去。)- 遗传算法:
模拟物竞天择的生物进化过程,通过维护一个潜在解的群体执行了多方向的搜索,并支持这些方向上的信息构成和交换。是以面为单位的搜索,比以点为单位的搜索,更能发现全局最优解。(在遗传算法中,有很多袋鼠,它们降落到喜玛拉雅山脉的任意地方。这些袋鼠并不知道它们的任务是寻找珠穆朗玛峰。但每过几年,就在一些海拔高度较低的地方射杀一些袋鼠,并希望存活下来的袋鼠是多产的,在它们所处的地方生儿育女。)(或者换个说法。从前,有一大群袋鼠,它们被莫名其妙的零散地遗弃于喜马拉雅山脉。于是只好在那里艰苦的生活。海拔低的地方弥漫着一种无色无味的毒气,海拔越高毒气越稀薄。可是可怜的袋鼠们对此全然不觉,还是习惯于活蹦乱跳。于是,不断有袋鼠死于海拔较低的地方,而越是在海拔高的袋鼠越是能活得更久,也越有机会生儿育女。就这样经过许多年,这些袋鼠们竟然都不自觉地聚拢到了一个个的山峰上,可是在所有的袋鼠中,只有聚拢到珠穆朗玛峰的袋鼠被带回了美丽的澳洲。)
实现
把那些总是爱走下坡路的袋鼠射杀,这是遗传算法的精粹!
所以我们总结出遗传算法的一般步骤:
while(开始循环直至找到满意的解):
1.评估每条染色体所对应个体的适应度。
2.遵照适应度越高,选择概率越大的原则,从种群中选择两个个体作为父方和母方。
3.抽取父母双方的染色体,进行交叉,产生子代。
4.对子代的染色体进行变异。
5.重复2,3,4步骤,直到新种群的产生。
结束循环。
评分函数及选择函数
1、物竞
适应度函数
我们的衡量标准是:袋鼠所在的海拔高度,越高越好
可直接用袋鼠的海拔高度作为适应性评分。
2、天择
选择函数:对环境适应性越强的越有利于生存下去,繁衍后代。
对适应性打分,分别为
x
1
x_1
x1,
x
2
x_2
x2,,,,,
x
i
x_i
xi,然后按照评分大小排序进行选择。
伪代码
具体求解TSP问题
背景:TSP旅行问题
问题:
从某物流中心用多台配送车辆向多个客户送货,每个客户的位置和货物需求量一定,每台配送车辆的载重量一定,其一次配送的最大行驶距离一定,要求合理安排车辆配送路线,使目标函数得到优化,并满足以下条件:
(1) 每条配送路径上各客户的需求量之和不超过配送车辆的载重量;
(2) 每条配送路径的长度不超过配送车辆一次配送的最大行驶距离;
(3) 每个客户的需求必须满足,且只能由一台配送车辆送货。
以配送总里程最短为目标函数
实例:
某物流中心有2 台配送车辆,其载重量均为8t ,车辆每次配送的最大行驶距离为50km ,配送中心(其编号为0) 与8 个客户之间, 8 个客户相互之间的距离 d i , j d_{i,j} di,j 为:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|
1 | 0 | 4 | 6 | 7.5 | 9 | 20 | 10 | 16 | 8 |
2 | 4 | 0 | 6.5 | 4 | 10 | 5 | 7.5 | 11 | 10 |
3 | 6 | 6.5 | 0 | 7.5 | 10 | 10 | 7.5 | 7.5 | 7.5 |
4 | 7.5 | 4 | 7.5 | 0 | 10 | 5 | 9 | 9 | 15 |
5 | 9 | 10 | 10 | 10 | 0 | 10 | 7.5 | 7.5 | 10 |
6 | 20 | 5 | 10 | 5 | 10 | 0 | 7 | 9 | 7.5 |
7 | 10 | 7.5 | 7.5 | 9 | 7.5 | 7 | 0 | 7 | 10 |
8 | 16 | 11 | 7.5 | 9 | 7.5 | 9 | 7 | 0 | 10 |
9 | 8 | 10 | 7.5 | 15 | 10 | 7.5 | 10 | 10 | 0 |
8 个客户的货物需求量 q j q_j qj为:q = [ 0, 1, 2, 1, 2, 1, 4, 2, 2 ],0值为配送中心
要求合理安排车辆配送路线,使配送总里程最短。
采用以下参数:
群体规模取20 ;
进化代数取25 ;
交叉概率取0.9 ;JCL = 0.9
变异概率取0.09 ;BYL = 0.09
变异时基因换位次数取5;JYHW = 5
实施爬山操作时爬山次数取20 ;PSCS = 20
对不可行路径的惩罚权重取100km;
对实例随机求解10 次;
代码
总流程迭代
1、初始化
2、计算适应度
3、产生新的集群
4、重复1、2、3
初始化
def __init__(self, rows, times, mans, cars, tons, distance, PW):
self.rows = rows '''排列个数'''
self.times = times '''迭代次数'''
self.mans = mans '''客户数量'''
self.cars = cars '''车辆总数'''
self.tons = tons '''车辆载重'''
self.distance = distance '''车辆一次行驶的最大距离'''
self.PW = PW '''当生成一个不可行路线时的惩罚因子'''
适应度函数计算
'''
计算适应度
计算的规则:每条配送路径要满足题设条件,并且目标函数即 车辆行驶的总里程越小,适应度越高
'''
def calFitness(self, line, isShow):
carTon = 0 '''当前车辆的载重'''
carDis = 0 '''当前车辆行驶的总距离'''
newTon = 0
newDis = 0
totalDis = 0
r = 0 '''表示当前需要车辆数'''
fore = 0 '''正在运送的客户编号'''
M = 0 '''当前的路径规划所需要的总车辆和总共拥有的车辆之间的差,如果大于0,表示是一个失败的规划,乘以一个很大的惩罚因子用来降低适应度'''
'''遍历每个客户点'''
for i in range(0, self.mans):
'''行驶的距离'''
newDis = carDis + self.d[fore][line[i]]
'''当前车辆的载重'''
newTon = carTon + self.q[line[i]]
'''如果已经超过最大行驶距离或者超过车辆的最大载重,切换到下一辆车'''
if newDis + self.d[line[i]][0] > self.distance or newTon > self.tons:
'''下一辆车'''
totalDis += carDis + self.d[fore][0] '''后面加这个d[fore][0]表示需要从当前客户处返程的距离'''
r += 1
fore = 0
i -= 1 '''表示当前这个点的配送还没有完成'''
carTon = 0
carDis = 0
else:
carDis = newDis
carTon = newTon
fore = line[i]
'''加上最后一辆车的距离和返程的距离'''
totalDis += carDis + self.d[fore][0]
if isShow:
print "总行驶里程为: %.1fkm" %(totalDis)
else:
# print "中间过程尝试规划的总行驶里程为: %.1fkm" %(totalDis)
pass
'''判断路径是否可用,所使用的车辆数量不能大于总车辆数量'''
if r - self.cars + 1 > 0:
M = r - self.cars + 1
'''目标函数,表示一个路径规划行驶的总距离的倒数越小越好'''
result = 1 / (totalDis + M * self.PW)
return result
新增集群