(8-3-02)优先级遍历(Priority-based Search)算法:Uniform Cost Search算法的基本思想

8.3.2  Uniform Cost Search算法的基本思想

实例8-4罗马尼亚之旅”的路径规划程序codes/8/london

(1)文件UK_cities.json包含了英国城市之间的距离信息。每个城市都有一个键值对,其中键是城市名称,值是一个字典,表示从当前城市到其他城市的距离。

{
    "aberdeen": {
        "aberystwyth": {"weight":427},
        "edinburgh":   {"weight":115},
        "penzance":    {"weight":663},
        "portsmouth":  {"weight":550},
        "sheffield":   {"weight":338},
        "swansea":     {"weight":502},
        "york":        {"weight":299}
    },
    "aberystwyth": {
        "brighton":    {"weight":249} ,
        "bristol":     {"weight":121} ,
        "carlisle":    {"weight":219} ,
        "dover":       {"weight":284} ,
        "newcastle":   {"weight":254} ,
        "nottingham":  {"weight":154} ,
        "oxford":      {"weight":156} ,
        "penzance":    {"weight":304} ,
        "sheffield":   {"weight":154} ,
        "swansea":     {"weight":75} ,
        "york":        {"weight":190}
    },
    "birmingham": {
        "brighton":    {"weight":159},
        "bristol":     {"weight":86},
        "cambridge":   {"weight":97},
        "cardiff":     {"weight":100},
        "carlisle":    {"weight":198},
        "portsmouth":  {"weight":141},
        "sheffield":   {"weight":75},
        "swansea":     {"weight":125},
        "york":        {"weight":125},
        "london":      {"weight":110}
    },
//省略部分代码
    "portsmouth":{
        "sheffield":   {"weight":214},
        "york":        {"weight":256}
    },
    "sheffield":{
        "swansea":     {"weight":196}
    },
    "swansea":{
        "york":        {"weight":248}
    }
}

在上述代码的每个城市的值字典中,键是目标城市的名称,值是一个包含"weight"键的字典,表示到目标城市的距离。例如,城市"aberdeen"到其他城市的距离信息如下:

  1. "aberystwyth":距离为427
  2. "edinburgh":距离为115
  3. "penzance":距离为663
  4. "portsmouth":距离为550
  5. "sheffield":距离为338
  6. "swansea":距离为502
  7. "york":距离为299

以此类推,其他城市之间的距离信息也以类似的格式给出。这种数据结构可以被用来构建一个图,其中城市是节点,距离是边,用来解决路径规划问题。

(2)文件graphCreation.py包含如下所示的两个功能:

  1. 函数show_weighted_graph用于绘制带权重的图形,其中节点代表城市,边代表城市之间的连接,边的权重表示连接的距离。
  2. 函数load_graph_from_file的功能是从给定的 JSON 文件中加载城市之间的距离信息,并创建一个带权重的图对象。
import networkx as nx
import matplotlib.pyplot as plt
import json

def show_weighted_graph(networkx_graph, node_size, font_size, fig_size):
    # 为了给每个节点分配足够的空间,使用给定的fig_size创建图形
    plt.figure(num=None, figsize=fig_size, dpi=80)
    plt.axis('off')
    # 计算每个顶点的位置,以便漂亮地显示它们
    nodes_position = nx.spring_layout(networkx_graph) 
    # 可以根据图形更改不同的布局,取图形中每条边对应的权重
    edges_weights = nx.get_edge_attributes(networkx_graph,'weight')
    # 绘制节点(您可以更改颜色)
    nx.draw_networkx_nodes(networkx_graph, nodes_position, node_size=node_size,  
                           node_color=["orange"] * networkx_graph.number_of_nodes())
    # 仅绘制边
    nx.draw_networkx_edges(networkx_graph, nodes_position, edgelist=list(networkx_graph.edges), width=2)
    # 添加权重
    nx.draw_networkx_edge_labels(networkx_graph, nodes_position, edge_labels=edges_weights)
    # 添加节点的标签
    nx.draw_networkx_labels(networkx_graph, nodes_position, font_size=font_size, 
                            font_family='sans-serif')
    plt.axis('off')
    plt.show()


def load_graph_from_file(filename):
    with open(filename) as uk_cities:
        dict_cities = json.load(uk_cities)
        return nx.Graph(dict_cities)

(3)文件ucs.py实现了Uniform Cost Search图搜索算法,用于在加权图中找到从起点到目标点的最低成本路径。此外,在文件ucs.py中还实现了函数show_weighted_graph,用于可视化加权图。在代码的末尾,使用load_graph_from_file从JSON文件中加载加权图,并使用uniformCostSearch函数寻找从伦敦到阿伯丁的最低成本路径,并通过show_weighted_graph函数可视化加权图。

def uniformCostSearch(graph, origin, goal):
    frontier = []  # 前沿队列
    frontierIndex = {}  # 保存前沿队列中元素的字典
    node = (0, origin, [origin])  # 起始节点,格式为(路径成本, 节点, 访问路径)
    frontierIndex[node[1]] = [node[0], node[2]]  # 在字典中保存起始节点的信息
    heapq.heappush(frontier, node)  # 将起始节点放入前沿队列
    explored = set()  # 已探索节点集合

    while frontier:
        if len(frontier) == 0:
            print("No solution found.")  # 若未找到路径,输出提示信息
            return None
        node = heapq.heappop(frontier)  # 弹出路径成本最低的节点
        del frontierIndex[node[1]]  # 从字典中删除已弹出的节点信息

        if node[1] == goal:  # 检查是否找到目标节点
            print("Goal node reached.")  # 输出找到目标节点的信息
            return node
        explored.add(node[1])  # 将节点标记为已探索
        neighbours = list(graph.neighbors(node[1]))  # 获取节点的邻居节点列表
        path = node[2]

        for child in neighbours:
            path.append(child)
            childNode = (node[0] + graph.get_edge_data(node[1], child)["weight"], child, path)  # 计算邻居节点的路径成本

            if child not in explored and child not in frontierIndex:  # 如果邻居节点未探索且不在前沿队列中
                heapq.heappush(frontier, childNode)  # 将邻居节点加入前沿队列
                frontierIndex[child] = [childNode[0], childNode[2]]  # 在字典中保存邻居节点信息
            elif child in frontierIndex:  # 如果邻居节点已在前沿队列中
                if childNode[0] < frontierIndex[child][0]:  # 检查新路径成本是否更低
                    nodeToRemove = (frontierIndex[child][0], child, frontierIndex[child][1])
                    frontier.remove(nodeToRemove)  # 删除原来的节点
                    heapq.heapify(frontier)
                    del frontierIndex[child]

                    heapq.heappush(frontier, childNode)  # 将更新后的节点加入前沿队列
                    frontierIndex[child] = [childNode[0], childNode[2]]
            path = path[:-1]

        print("Explored nodes: {}".format(explored))  # 打印已探索节点集合

def draw_path(graph, path):
    pos = nx.spring_layout(graph)  # 计算节点的布局
    nx.draw(graph, pos, with_labels=True, node_color="orange", node_size=1000)  # 绘制节点
    nx.draw_networkx_nodes(graph, pos, nodelist=path, node_color='orange', node_size=1000)  # 绘制路径节点
    edges = [(path[i], path[i + 1]) for i in range(len(path) - 1)]  # 提取路径中的边
    nx.draw_networkx_edges(graph, pos, edgelist=edges, edge_color='red', width=2)  # 绘制路径边
    plt.axis('off')  # 关闭坐标轴
    plt.show()  # 显示图形

uk_map = gc.load_graph_from_file("UK_cities.json")  # 从JSON文件中加载图
solution = uniformCostSearch(uk_map, "london", "aberdeen")  # 使用统一代价搜索算法寻找最低成本路径

if solution:
    print("SOLUTION: {}".format(solution))
    path_nodes = solution[2]  # 获取最低成本路径节点列表
    print("Path nodes: {}".format(path_nodes))  # 打印最低成本路径节点列表
    draw_path(uk_map, path_nodes)  # 绘制最低成本路径

上述代码的实现流程如下所示。

  1. 首先,实现了Uniform Cost Search算法函数uniformCostSearch,用于在给定图中寻找两个节点之间的最低成本路径。算法使用了前沿队列来管理待探索的节点,并使用字典来追踪前沿队列中的节点信息。在搜索过程中,算法从前沿队列中弹出路径成本最低的节点,并将其邻居节点加入队列,直到找到目标节点或者搜索完成。
  2. 接着,加载了地图数据,并调用统一代价搜索算法寻找从伦敦到阿伯丁的最低成本路径。在搜索过程中,会打印输出如下所示的详细的探索信息,包括探索的节点和路径成本。
Explored nodes: {'london'}
Explored nodes: {'london', 'brighton'}
Explored nodes: {'cambridge', 'london', 'brighton'}
Explored nodes: {'oxford', 'cambridge', 'london', 'brighton'}
Explored nodes: {'oxford', 'dover', 'brighton', 'cambridge', 'london'}
Explored nodes: {'oxford', 'dover', 'brighton', 'cambridge', 'london', 'portsmouth'}
Explored nodes: {'oxford', 'dover', 'brighton', 'cambridge', 'london', 'birmingham', 'portsmouth'}
Explored nodes: {'oxford', 'dover', 'brighton', 'bristol', 'cambridge', 'london', 'birmingham', 'portsmouth'}
Explored nodes: {'oxford', 'dover', 'brighton', 'nottingham', 'bristol', 'cambridge', 'london', 'birmingham', 'portsmouth'}
Explored nodes: {'oxford', 'dover', 'brighton', 'nottingham', 'bristol', 'cambridge', 'cardiff', 'london', 'birmingham', 'portsmouth'}
Explored nodes: {'oxford', 'dover', 'brighton', 'nottingham', 'bristol', 'cambridge', 'cardiff', 'london', 'birmingham', 'portsmouth', 'sheffield'}
Explored nodes: {'oxford', 'dover', 'brighton', 'nottingham', 'bristol', 'cambridge', 'cardiff', 'london', 'birmingham', 'portsmouth', 'sheffield', 'exeter'}
Explored nodes: {'oxford', 'dover', 'brighton', 'nottingham', 'bristol', 'cambridge', 'hull', 'cardiff', 'london', 'birmingham', 'portsmouth', 'sheffield', 'exeter'}
Explored nodes: {'oxford', 'dover', 'brighton', 'nottingham', 'bristol', 'cambridge', 'leeds', 'hull', 'cardiff', 'london', 'birmingham', 'portsmouth', 'sheffield', 'exeter'}
Explored nodes: {'oxford', 'dover', 'brighton', 'nottingham', 'bristol', 'cambridge', 'leeds', 'hull', 'cardiff', 'liverpool', 'london', 'birmingham', 'portsmouth', 'sheffield', 'exeter'}
Explored nodes: {'oxford', 'dover', 'brighton', 'nottingham', 'bristol', 'cambridge', 'leeds', 'hull', 'manchester', 'cardiff', 'liverpool', 'london', 'birmingham', 'portsmouth', 'sheffield', 'exeter'}
Explored nodes: {'oxford', 'dover', 'brighton', 'nottingham', 'bristol', 'cambridge', 'leeds', 'hull', 'manchester', 'cardiff', 'liverpool', 'london', 'birmingham', 'portsmouth', 'swansea', 'sheffield', 'exeter'}
Explored nodes: {'oxford', 'dover', 'york', 'brighton', 'nottingham', 'bristol', 'cambridge', 'leeds', 'hull', 'manchester', 'cardiff', 'liverpool', 'london', 'birmingham', 'portsmouth', 'swansea', 'sheffield', 'exeter'}
Explored nodes: {'oxford', 'nottingham', 'bristol', 'manchester', 'liverpool', 'leeds', 'london', 'portsmouth', 'sheffield', 'york', 'brighton', 'hull', 'aberystwyth', 'birmingham', 'dover', 'cambridge', 'cardiff', 'exeter', 'swansea'}
Explored nodes: {'oxford', 'nottingham', 'bristol', 'manchester', 'newcastle', 'liverpool', 'leeds', 'london', 'portsmouth', 'sheffield', 'york', 'brighton', 'hull', 'aberystwyth', 'birmingham', 'dover', 'cambridge', 'cardiff', 'exeter', 'swansea'}
Explored nodes: {'oxford', 'nottingham', 'bristol', 'manchester', 'newcastle', 'liverpool', 'carlisle', 'leeds', 'london', 'portsmouth', 'sheffield', 'york', 'brighton', 'hull', 'aberystwyth', 'birmingham', 'dover', 'cambridge', 'cardiff', 'exeter', 'swansea'}
Explored nodes: {'oxford', 'nottingham', 'bristol', 'manchester', 'newcastle', 'liverpool', 'penzance', 'carlisle', 'leeds', 'london', 'portsmouth', 'sheffield', 'york', 'brighton', 'hull', 'aberystwyth', 'birmingham', 'dover', 'cambridge', 'cardiff', 'exeter', 'swansea'}
Explored nodes: {'oxford', 'edinburgh', 'nottingham', 'bristol', 'manchester', 'newcastle', 'liverpool', 'penzance', 'carlisle', 'leeds', 'london', 'portsmouth', 'sheffield', 'york', 'brighton', 'hull', 'aberystwyth', 'birmingham', 'dover', 'cambridge', 'cardiff', 'exeter', 'swansea'}
Explored nodes: {'oxford', 'edinburgh', 'nottingham', 'bristol', 'manchester', 'newcastle', 'liverpool', 'penzance', 'glasgow', 'carlisle', 'leeds', 'london', 'portsmouth', 'sheffield', 'york', 'brighton', 'hull', 'aberystwyth', 'birmingham', 'dover', 'cambridge', 'cardiff', 'exeter', 'swansea'}
Goal node reached.
SOLUTION: (502, 'aberdeen', ['london', 'cambridge', 'york', 'aberdeen'])
Path nodes: ['london', 'cambridge', 'york', 'aberdeen']
  1. 最后,可视化展示地图数据信息,并将找到的最低成本路径用红色线条标识出来,以便直观地观察到路径。如图8-3所示。

图8-3  地图数据和路径可视化图

  • 18
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农三叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值