IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python:遗传算法最优路径 -> 正文阅读

[Python知识库]Python:遗传算法最优路径

Hello,大家好!读研前写过一篇遗传算法的代码,比较简单,算是个入门,当时就有想用它来解决最优路径的问题,上算法导论课时碰巧有听到同学有分享过,但由于自己研究的方向不是这块,就没有再弄,结果今年的华为杯数学建模竞赛F题居然有所涉及,真是用时方恨晚,最近即将毕业,也稍微空闲些了,就再用遗传算法慢慢捡回我的公众号。今天分享的是如何用遗传算法进行最优路径求解问题!

用python实现遗传算法这是两年前写的遗传算法,做了一个简单的介绍,感兴趣的小伙伴可以翻看。

问题

现在有 n n n个地址的坐标,以第一个为起点,途径所有地址,再回到起点,所有地方仅去一次,规划最短路径。

思路与Python实现

编码

首先先解决编码问题,与上篇文章不同,这次解决的是路径规划问题,有的是一个一个的坐标点,因此我们采用“符号编码”代表这些坐标点,染色体上的编码顺序代表路径顺序。

我们随机生成十组坐标,用作本文的示范:

a = list(range(-5,5))
b = list(range(-4,6))
random.shuffle(a)
random.shuffle(b)
local = list(zip(a,b))
序号横坐标纵坐标
0-5-2
1-34
245
3-4-3
4-11
50-1
613
72-4
8-20
932

这些序号就可以用作我们符号编码,例如[0,1,2,3,4,5,6,7,8,9]

个体评价

个体评价也就是我们的目标函数,用来区分群体中个体好坏的标准。我们路径规划问题是寻找最短行驶路径,这里用两点之间距离公式进行度量,然后按照染色体上的编码顺序依次累加两点之间的距离:

def dis(start, end):
    # 两点之间距离
    return np.sqrt((end[0]-start[0])**2+(end[1]-start[1])**2)

def fuc(x):
    dis_sum = dis((0,0),local[x[0]])
    for i in range(1,len(x)):
        dis_sum += dis(local[x[i-1]],local[x[i]])
    dis_sum += dis(local[x[-1]],(0,0))
    return dis_sum

def get_fitness(pops):
    return list(map(fuc,pops))

注:本文是以原点作为线路的起点和终点,形成闭环,不同情况要不同设计

选择

选择算子的作用是对个体进行优胜劣汰:从父代群体中选取一些适应度高个体遗传到下一代群体中,这次采用锦标赛选择策略

锦标赛选择策略:从种群中随机采样 s s s个个体(有放回抽样)进行PK,其中适应度值最优的个体胜出,成为下一代的父代基因,进行 k k k轮,得到 k k k个优质父代。

这样最差的个体永远不会存活,并且计算简单,不容易陷入局部最优,可以达到更好的求解效果。

def select(pops):
    k = round(np.sqrt(len(pops)+0.25)+0.5)
    fitness = get_fitness(pops)
    father_pops = []
    for i in range(k):
        min_index = np.array(fitness).argmin()
        father_pops.append(pops.pop(min_index))
        fitness.pop(min_index)
    return father_pops

交叉

接下来就到了整个算法的重头,不同于上一篇文章,单纯地使用两点交叉即可,路径规划中,所有的地点要秉持“不遗漏,不重复”的原则,如果单纯地交叉,会导致地点重复或遗漏。因此就在两点交叉的过程中加一个映射过程,如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rTQ4O7x8-1671273828116)(.\figures\两点交叉策略示例.png)]

def yinshe(dic, x): # 映射
    while x in dic.keys():
        x = dic[x]
    return x

def jiaocha(Lis1, Lis2): # 两点交叉
    lis1 = Lis1.copy()
    lis2 = Lis2.copy()
    n = len(lis1)
    cross_points_1 = np.random.randint(low=0, high=n-1)
    cross_points_2 = np.random.randint(low=cross_points_1+1, high=n)
    
    yinshe2 = dict(zip(lis1[cross_points_1: cross_points_2],lis2[cross_points_1: cross_points_2]))
    yinshe1 = dict(zip(lis2[cross_points_1: cross_points_2],lis1[cross_points_1: cross_points_2]))

    lis1[cross_points_1: cross_points_2], lis2[cross_points_1: cross_points_2] = lis2[cross_points_1: cross_points_2], lis1[cross_points_1: cross_points_2]
    
    lis1[:cross_points_1] = list(map(lambda x: yinshe(yinshe1, x), lis1[:cross_points_1]))
    lis1[cross_points_2:] = list(map(lambda x: yinshe(yinshe1, x), lis1[cross_points_2:]))
    lis2[:cross_points_1] = list(map(lambda x: yinshe(yinshe2, x), lis2[:cross_points_1]))
    lis2[cross_points_2:] = list(map(lambda x: yinshe(yinshe2, x), lis2[cross_points_2:]))
    return lis1, lis2

细心的小伙伴肯定就发现了,为什么映射过程的代码里有一个while循环,这个地方是我在实验的过程中发现的一个细节,如果单靠一次映射,并不能保证所重复的点都映射完,就像上图中的例子,明明 9 9 9对应的是 4 4 4,但是 4 4 4本身也在交叉的基因之中,并没能把9映射到外面,故需要三次映射,即 9 → 4 → 3 → 6 9 \rightarrow 4 \rightarrow 3 \rightarrow 6 9436,因此才把外面的 9 9 9替换为了 6 6 6

变异

是对群体中的个体的某些基因座上的基因值作变动,模拟生物在繁殖过程,新产生的染色体中的基因会以一定的概率发生突变。这样的设计可以很好地避免局部最优的情况。

def mutation(pop, MUTATION_RATE=0.003):
    if np.random.rand() < MUTATION_RATE:
        n = len(pop)
        cross_points_1 = np.random.randint(low=0, high=n-1)
        cross_points_2 = np.random.randint(low=cross_points_1+1, high=n)
        pop[cross_points_1], pop[cross_points_2] = pop[cross_points_2], pop[cross_points_1]
    return pop

整合

有了“个体评价”、“选择”、“交叉”、“变异”这些模块,就可以实现一代代的遗传进化:

def evolution(pops):
    father_pops = select(pops)
    k = len(father_pops)
    new_pops = []
    for i in range(k-1):
        for j in range(i+1,k):
            son1, son2 = jiaocha(father_pops[i], father_pops[j])
            son1 = mutation(son1, MUTATION_RATE=0.3)
            son2 = mutation(son2, MUTATION_RATE=0.3)
            new_pops.append(son1)
            new_pops.append(son2)
    return new_pops

效果演示

最后只需要把前面的内容整合在一起即可,因为问题比较简单,所以遗传的代数设置50就足够了。

local = [(-5, -2), (-3, 4), (4, 5), (-4, -3), (-1, 1), (0, -1), (1, 3), (2, -4), (-2, 0), (3, 2)]
pops = initpop(N, list(range(10)))
for _ in range(50):
    pops = evolution(pops)
print(min(get_fitness(pops)))

然后我们把整个遗传过程可视化出来,效果如所想的一样,最短的路径就是围着转一圈,整个过程感觉还是非常神奇的,如果有想要这个可视化代码的小伙伴,可在文末获得,文章中就不再赘述了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w8ikJ09L-1671273828118)(.\figures\演示.gif)]

获得代码

以下是我的个人公众号,本文完整代码已上传,关注公众号回复“遗传算法最优路径”,即可获得,谢谢大家支持。
在这里插入图片描述

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-12-25 11:04:55  更:2022-12-25 11:05:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/20 7:14:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码