zoukankan      html  css  js  c++  java
  • [大创]一步一步写路径规划并绘图可视化 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

  • 相关阅读:
    洛谷—— P3353 在你窗外闪耀的星星
    洛谷—— P1238 走迷宫
    洛谷—— P1262 间谍网络
    9.8——模拟赛
    洛谷—— P1189 SEARCH
    算法
    May 22nd 2017 Week 21st Monday
    May 21st 2017 Week 21st Sunday
    May 20th 2017 Week 20th Saturday
    May 19th 2017 Week 20th Friday
  • 原文地址:https://www.cnblogs.com/dragonbean/p/13498421.html
Copyright © 2011-2022 走看看