• [大创]一步一步写路径规划并绘图可视化 IV (solutions.py)


    介绍

    作者:dragonbean
    从名字solutions就能看出,这个py文件是一个算法集,内涵了多种不同的solution算法,下面就来对其中的算法进行讲解。算法所处理的对象是 visual.py 传来的可视点的字典。
    {障碍物顶点:[可视点,可视点,可视点,...],...} 数据结构是:{(x0,y0):[(x1,y1),(x3,y3),...],(x1,y1):[(x0,y0),...],...}

    算法一

    全部递归 全局最优
    对某个点的所有可视点进行遍历,遍历时又对遍历到的点的所有可视点进行遍历(简单说就是递归),直到被遍历到的点是终止点,就返回到上层递归里去。

    '''
          author:dragonbean
          href:https://www.cnblogs.com/dragonbean/p/13498421.html
          blog:https://www.cnblogs.com/dragonbean/
    '''
    def solution_one(start, end, graph):
        def __searchPath(start, end, graph):
            results = []
            __generatePath(graph, [start,0], end, results)
            results.sort(key=lambda x:x[-1])
            return results
    
        def __generatePath(graph, path, end, results):
            current = path[-2]
            if current == end:
                results.append(path)
                #print(path)
            else:
                for n in graph[current]:
                    if n not in path and path[-1]<(minDistance*1.5):                                                    #递归深度为 3/2起始点终止点间距离
                        distance = path[-1] + math.sqrt((n[0]-path[-2][0])**2 + (n[1]-path[-2][1])**2)                  #增量计算不同路径的距离
                        __generatePath(graph, path[:-1]+[n,distance], end, results)
    
        def showPath(results):
            print('The path from ', results[0][0], 'to' , results[0][-1], 'is :')
            for path in results:
                print(path)
        
        minDistance = math.sqrt((end[1]-start[1])**2 + (end[0]-start[0])**2)
        results = __searchPath(start, end, graph)
        if __name__ == "__main__":
            showPath(results)
        return results[0]
    

    这里需要注意的是,我对递归的深度做了优化处理,导致递归量大幅减小:在递归的过程中记录路程长度,并与起始点、终止点间距离的1.5倍做比较,小于这个数值的将继续递归下去,大于的将直接返回递归。这么处理可以有效避免多余的内存开销,只是这个1.5倍不对所有情况适用,属于这里的bug。

    算法二

    直接寻路 局部最优
    从起始点开始,遍历该点的所有可视点,计算到该可视点的直线距离与该可视点到终止点的直线距离之和,比较后得出最优的可视点,作为下一次递归的“起始点”,一步一步直到找到终止点end。

    '''
          author:dragonbean
          href:https://www.cnblogs.com/dragonbean/p/13498421.html
          blog:https://www.cnblogs.com/dragonbean/
    '''
    def solution_two(start, end, graph):
        print(graph)
        #还阔以再优化,再计算多一个点,就更趋于最优了
        def __findNext(start,end,graph, path):
            minDistance = [None,None]
            for each in graph[start]:
                if each == end:
                    path.append(end)
                    return
                else:
                    distance = math.sqrt((each[0]-start[0])**2+(each[1]-start[1])**2)+math.sqrt((end[0]-each[0])**2+(end[1]-each[1])**2)
                    if minDistance[1]==None or minDistance[1]>distance:
                        minDistance = [each,distance]
            print(minDistance[0])
            path.append(minDistance[0])
            __findNext(minDistance[0],end, graph, path)
    
        path = [start]
        __findNext(start, end, graph, path)
        print(path)
        return path
    

    还可以对这个算法进行优化,正如注释里所说的,多计算一层的点(就是可视点的下一层所有可视点的“距离”也计算在内进行比较),以选出最优的第一个可视点,接着对它进行递归。

    '''
          author:dragonbean
          href:https://www.cnblogs.com/dragonbean/p/13498421.html
          blog:https://www.cnblogs.com/dragonbean/
    '''
    def solution_three(start, end, graph):
        def __findNext(start,end,graph, path):
            minDistance = [None,None]
            for each in graph[start]:
                if each in path:
                    continue
                elif each == end:
                    path.append(end)
                    return
                next_min_distance = None
                tmp_list = graph[each][:]
                try:
                    tmp_list.remove(start)
                    print(start)
                    print(tmp_list)
                except:
                    pass
                for i in tmp_list:
                    if i == end:
                        path.extend([each,i])
                        return
                    next_distance = math.sqrt((end[0]-i[0])**2+(end[1]-i[1])**2) + math.sqrt((i[0]-each[0])**2+(i[1]-each[1])**2)
                    if next_min_distance==None or next_min_distance>next_distance:
                        next_min_distance = next_distance
                distance = next_min_distance + math.sqrt((each[0]-start[0])**2+(each[1]-start[1])**2)
                if minDistance[1]==None or minDistance[1]>distance:
                    minDistance = [each,distance]
            path.append(minDistance[0])
            __findNext(minDistance[0],end, graph, path)
    
        path = [start]
        __findNext(start, end, graph, path)
        print(path)
        return path
    

    还有一种优化,是将算法一和算法二结合起来,先确定一条可行的路径,将这个可行路径的值作为算法一递归深度的阈值,对算法一的全局最优做优化。

    '''
          author:dragonbean
          href:https://www.cnblogs.com/dragonbean/p/13498421.html
          blog:https://www.cnblogs.com/dragonbean/
    '''
    def solution_four(start, end, graph):
        def __searchPath(start, end, graph):
            results = []
            __generatePath(graph, [start,0], end, results)
            results.sort(key=lambda x:x[-1])
            return results
    
        def __generatePath(graph, path, end, results):
            current = path[-2]
            if current == end:
                results.append(path)
                #print(path)
            else:
                for n in graph[current]:
                    if n not in path and path[-1]<(minDistance*1.5):
                        distance = path[-1] + math.sqrt((n[0]-path[-2][0])**2 + (n[1]-path[-2][1])**2)                  #增量计算不同路径的距离
                        __generatePath(graph, path[:-1]+[n,distance], end, results)
    
        def showPath(results):
            print('The path from ', results[0][0], 'to' , results[0][-1], 'is :')
            for path in results:
                print(path)
        
        #先得到一条可行的路径,将它的长度作为阈值。
        prePath = solution_three(start,end,graph)
        minDistance = 0.0
        for i in range(len(prePath)-1):
            minDistance += math.sqrt((prePath[i+1][0]-prePath[i][0])**2+(prePath[i+1][1]-prePath[i][1])**2)
        results = __searchPath(start, end, graph)
        return results[0]
    

    SPFA算法

    队列优化算法
    基于spfa(Shortest Path Faster Algorithm)算法思想编写,确实是路程最短、耗时最少的算法(就我之前写的这些算法而言)。可真是太棒了这个算法。至于这个算法本身我就不过多的赘述,可自行百度哦。
    解释不下去了,我还是讲一下spfa

    '''
          author:dragonbean
          href:https://www.cnblogs.com/dragonbean/p/13498421.html
          blog:https://www.cnblogs.com/dragonbean/
    '''
    def solution_spfa(start, end, graph):
        #核心代码
        def spfa(point):
            visualpoint = graph[point]
            #如果这个点可以直接可视终止点end,那么这个点到end点的距离一定是最短的,两点之间直线最短。
            if end in visualpoint:
                distance = math.sqrt((point[0]-end[0])**2 + (point[1]-end[1])**2)
                distance += roadDict[point][1]
                if roadDict.get(end):
                    oldDistance = roadDict[end][1]
                    if distance < oldDistance:
                        roadDict[end] = [point,distance]
                else:
                    roadList.append(end)
                    roadDict[end] = [point,distance]
            else:
                #一般情况处理【核心】
                for each in visualpoint:
                    distance = math.sqrt((point[0]-each[0])**2 + (point[1]-each[1])**2)
                    distance += roadDict[point][1]
                    if roadDict.get(each):
                        oldDistance = roadDict[each][1]
                        if distance < oldDistance:
                            roadDict[each] = [point,distance]
                    else:
                        roadList.append(each)
                        roadDict[each] = [point,distance]
        
        #反推出路径
        def getroad():
            road = [end]
            node = end
            while True:
                nextnode = roadDict.get(node)[0]
                road.append(nextnode)
                node = nextnode
                if nextnode == start:
                    break
            road.reverse()
            return road
        
        roadDict = {start:[start,0]}
        roadList = [start]
        #这个遍历是实时更新的,不安全。
        for node in roadList:
            if node != end:
                spfa(node)
        
        result = getroad()
        return result
    

    基于spfa思想,结合程序本身。我自己编写了一个简化版的spfa代码,不同于网上的spfa代码(主要是我不太看得懂,且没有耐心研读....)。

    总结

    solutions.py 看似是一个算法集合,但是自我将spfa算法放进去之后,经过测试,除了spfa,其他的算法我都看不上了(简直瞧不起自己),算法一的优化存在极大的问题,在某些(很多)情况下,1.5倍的两直线距离卡递归深度是简陋的,是极不稳定的,是跑不出来结果的。而算法二的直接寻路,局部最优的思想得到的并不一定是全局最优的结果,也就是说,他虽然在任何情况下都能跑出来结果,但是规划得到的路径不一定是最短路径,最优路径。而当前两个算法结合之后,以算法二寻得的次优路径值作为递归深度的阈值,计算速度还是相当可观的,计算结果也是全局最优的。
    但是,but,but,but,spfa算法,不论是计算速度和计算结果,都是最优的,相比于算法一和算法二的结合,spfa的速断还是更快。毕竟虽然卡了递归深度,但是仍然要进行算法一的众多递归,而且先执行算法二寻找出一条可行的次优路径就已经浪费掉了时间,而之后又要再寻找一次最优路径,就相当于是执行了两次寻路算法,逻辑上都比spfa慢2倍,更别说递归的时间复杂度不是线性的。
    因此以后的实验,都会选择用spfa来实现。
    转载请注明出处:https://www.cnblogs.com/dragonbean/p/13498421.html

  • 相关阅读:
    浩然战法--黄金柱选股
    《含泪活着》主人公一一丁尚彪,叙述在日本当黑户口的危险经历,美国《世界日报》2017年3月16日连载
    Java8与JDK8和JDK1.8有什么区别?
    ThreadLocal
    什么是jsonp
    Java中比较对象大小的两种实现方式
    MySQL教程之concat以及group_concat的用法
    多级树形结构和sql查询实现
    mysql树形结构递归查询
    Maven配置教程
  • 原文地址:https://www.cnblogs.com/dragonbean/p/13498421.html
Copyright © 2020-2023  润新知