mediarecorder路径设置为localsocket_leetcode刷题(六):路径规划问题

fb660318612ee15a57f2fb1eb159d1e5.png

由于最近接触了大量关于路径规划的算法,所以这一期我们来讨论下路径规划的问题!~!

首先,我要介绍下关于路径规划问题的一些基本概念。

在最短路径问题中,我们往往给定一个带权重的有向图,G=(V,E),以及权重函数w:E->R,改权重函数将每条边映射到实数值的权重上。

松弛操作

对于一条边(u,v)的松弛过程就是:首先测试一下对s到v的最短路径进行改善。

测试方法就是:将从结点s到结点u的最短路径加上结点u到结点v的权重值,并与当前结点s到结点v的最短路径进行比较。如果前者更小,就进行更新。

接下来我会介绍下几种主要的路径规划算法:

Dijkstra算法

Dijkstra算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。

Dijkstra算法采用的是一种贪心的策略:

他首先声明一个数组dis来保存源点s到各个结点的最短距离,以及一个保存已经找到最短的结点的集合T。

初始后,他先将s的路径权重设置为0,再将对于s能直接到达的u结点的权重设置为w,即dis[u]=w,同时把其他(s不能直接到的结点)路径长度设置为无穷大。此时T里面只包含u一个结点。

此时dis[]里面的值是否就是s结点能到达的最短路径呢?不着急,我们先看看新加入的结点是否可以到达其他节点,并且看看经由此节点到达其他结点的路径是否比从s源点直接到达更短,也就是一个松弛的过程。

然后,重复上述动作,直到T中包含了图的所有顶点。

此算法有也衍生出很多应用在不同场景下的算法,比如说用于稀疏图的Johnson算法。Johnson算法使用技术称之为重新赋予权重。

原理如下:

如果图中所有边的权重赋值为非负数,我们可以通过对每个结点对运行一次Dijkstra算法来找到所有结点对之间的最短路径。

如果图中包含有负数的权重的边,且不包含权重为负的环路,那么我们只要计算出一组新的非负权重值,然后用相同的方法重新计算即可。

A*算法

A*算法的关键是下面这个等式:

F = G + H

其中,G为从起点 A 移动到指定方格的移动代价,沿着到达该方格而生成的路径。H为从指定的方格移动到终点 B 的估算成本。

步骤如下:

  1. 我们从起点 A 开始,并把它就加入到一个open list( 开放列表 ) 中。现在 open list 里只有一项,它就是起点 A ,后面会慢慢加入更多的项。基本上 open list 是一个待检查的方格列表。
  2. 查看与起点 A 相邻的方格,把其中可走的或可到达的方格也加入到 open list 中。把起点 A设置为这些方格的父亲。
  3. 把A从 open list 里取出,放到 close list 中,close list 中的每个元素都是现在不需要再关注的。
  4. 检查所有与它相邻的方格,忽略其中在 close list 中的元素,如果方格不在open lsit 中,则把它们加入到 open list 中。同时把我们选定的方格设置为这些新加入的方格的父亲。
  5. 如果某个相邻的方格已经在 open list 中,则检查这条路径是否更优,也就是说经由当前方格 ( 我们选中的方格 ) 到达那个方格是否具有更小的 G 值。如果没有,不做任何操作。

Ok ,现在你已经看完了整个的介绍,再来看看我们把所有步骤放在一起的伪代码:

把起点加入 open list 。

重复如下过程:

a.遍历 open list ,查找 F 值最小的节点,把它作为当前要处理的节点。

b.把这个节点移到 close list 。

c.对当前方格的 8 个相邻方格的每一个方格?

◆如果它是不可抵达的或者它在 close list 中,忽略它。否则,做如下操作。

◆如果它不在 open list 中,把它加入 open list ,并且把当前方格设置为它的父亲,记录该方格的F、G和H 值。

◆如果它已经在 open list 中,检查这条路径 ( 即经由当前方格到达它那里 ) 是否更好,用 G 值作参考。更小的 G 值表示这是更好的路径。如果是这样,把它的父亲设置为当前方格,并重新计算它的 G 和 F 值。

d.停止,当你

◆把终点加入到了 open list 中,此时路径已经找到了,或者

◆查找终点失败,并且 open list 是空的,此时没有路径。

e.保存路径。从终点开始,每个方格沿着父节点移动直至起点,这就是你的路径。

蚁群算法

蚁群算法,顾名思义,就是模拟蚁群寻找最佳路径的一种算法。

蚂蚁行进时会释放出信息素,世上本没路,走的蚂蚁多了,信息素自然就浓了,路也自然就有了。路径越短,蚂蚁往返越快,信息素自然就越浓,所以信息素最浓的路径就是最佳路径了。

这里以TSP问题为例,算法设计的流程如下:

  1. 对相关参数进行初始化,包括蚁群规模、信息素因子、启发函数因子、信息素挥发因子、信息素常数、最大迭代次数等,以及将数据读入程序,并进行预处理:比如将城市的坐标信息转换为城市间的距离矩阵。
  2. 随机将蚂蚁放于不同出发点,对每个蚂蚁计算其下个访问城市,直到有蚂蚁访问完所有城市。
  3. 计算各蚂蚁经过的路径长度Lk,记录当前迭代次数最优解,同时对路径上的信息素浓度进行更新。
  4. 判断是否满足终止条件(如达到最大迭代次数),若否,返回步骤2;是,结束程序。
  5. 输出结果,并根据需要输出寻优过程中的相关指标,如运行时间、收敛迭代次数等。

当然,真实的算法没有这么简单,我们还需要考虑到很多细节,我简单缕一缕吧。

随机问题:

在迭代中,我们将一部分蚂蚁根据信息素浓度进行任务分配,另一部分采用随机分配策略。

因为如果每只蚂蚁都将任务分配给信息素浓度最高的节点处理,那么就会出现停滞现象。也就是算法过早地收敛至一个局部最优解,无法发现全局最优解。 因此需要一部分蚂蚁遵循信息素最高的分配策略,还需要一部分蚂蚁遵循随机分配的策略,以发现新的局部最优解。

信息素问题:

我们模拟真实的蚁群中,蚂蚁分泌的信息素会随着时间的推移而渐渐衰减。所以在算法中,我们使得信息素每完成一次迭代后进行衰减,但在一次迭代过程中,信息素浓度保持不变。

我们同时也模拟真实的蚁群中,蚂蚁会在行进过程中分泌信息素。所以在算法中,我们使得算法每完成一次迭代后,就将蚂蚁经过的路径上增加信息素q,但在一次迭代过程中,信息素浓度不变。

局部最优解问题:

在整个蚁群算法中,每一次迭代都会产生当前的最优分配策略,也就是“局部最优解”。迭代的次数越多,那么局部最优解就越接近于全局最优解。但是,迭代次数过多会造成时间的消耗和性能上的开销,从而无法满足任务的调度。但迭代次数太少了,可能得到的并不是全局最优解。这个问题有两种解决办法:

1.限定迭代次数

为了避免过多的迭代,我们可以事先设置一个迭代次数,从而迭代了这么多次后,就把当前的局部最优解当作全局最优解。

2.设置误差允许范围

我们还可以事先设置一个允许的误差范围。当迭代N次后,当前最优的任务处理时间在这个允许范围之内了,那么就停止迭代。

模拟退火算法

模拟退火算法是通过赋予搜索过程一种时变且最终趋于零的概率突跳性,从而可有效避免陷入局部极小并最终趋于全局最优的串行结构的优化算法。

是不是没看懂?我还是喜欢用一个好玩的栗子来说明模拟退火算法:

普通贪婪算法:兔子朝着比现在低的地方跳去。它找到了不远处的最低的山谷。但是这座山谷不一定最低的。这就是普通贪婪算法,它不能保证局部最优值就是全局最优值。

模拟退火:兔子喝醉了。它随机地跳了很长时间。这期间,它可能走向低处,也可能踏入平地。但是,它渐渐清醒了并朝最低的方向跳去。这就是模拟退火。

贪婪算法可以看我之前的文章:

leetcode刷题(一):贪心算法​mp.weixin.qq.com

说白了模拟退火其实也是一种贪婪算法,但是它的搜索过程引入了随机因素。模拟退火算法以一定的概率来接受一个比当前解要差的解,因此有可能会跳出这个局部的最优解,达到全局的最优解。

那模拟退火又是怎么回事呢?

控制参数t模拟了物理过程中的温度T,目标函数模拟了物理过程中的能量E,而解模拟了粒子群的微观状态。即得到解组合优化问题的模拟退火演算法:对于控制参数的每一取值,算法持续进行”产生新解—判断—接受/舍弃”的迭代,同时逐步衰减 t 值的过程就对应着固体在某一恒定温度下趋于热平衡的过程,也就是执行了一次Metropolis算法。算法终止时的当前解即为所得近似最优解。

总结起来就是:

若f( Y(i+1) ) <= f( Y(i) ) (即移动后得到更优解),则总是接受该移动;

若f( Y(i+1) ) > f( Y(i) ) (即移动后的解比当前解要差),则以一定的概率接受移动,而且这个概率随着时间推移逐渐降低(逐渐降低才能趋向稳定)。如果这个坡特别长,那么很有可能最终我们并不会翻过这个坡。如果它不太长,这很有可能会翻过它,这取决于衰减 t 值的设定。

关于路径规划问题leetcode上没有太多的题目可以参考,所以我只找了一题。感兴趣的朋友可以自己把各个算法用代码敲一敲!~!

62. 不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

问总共有多少条不同的路径?

说明:m 和 n 的值均不超过 100。

示例 1:

输入: m = 3, n = 2

输出: 3

解释:

从左上角开始,总共有 3 条路径可以到达右下角。

1. 向右 -> 向右 -> 向下

2. 向右 -> 向下 -> 向右

3. 向下 -> 向右 -> 向右

示例 2:

输入: m = 7, n = 3

输出: 28

class Solution(object):
    def uniquePaths(self, m, n):
        """
        :type m: int
        :type n: int
        :rtype: int
        """
        if n < 0 and m < 0:
            return 0
        res = [0 for i in range(n)]
        res[0] = 1
        for i in range(m):
            for j in range(1,n):
                res[j] += res[j-1]
        return res[-1]

差不多就是这样了,希望本文能帮到你!~!

最后打个小广告,我的公众号,喜欢写点学习中的小心得,不介意可以关注下!~!

167503703d616e66cb781cdc84083897.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值