“荔枝使”的难题怎么破:A*运输路径算法编程实践

发布于:2025-06-23 ⋅ 阅读:(15) ⋅ 点赞:(0)

请在此添加图片描述
原文首发请访问:https://cloud.tencent.com/developer/article/2533317

荔枝最初被称为“离支”,亦作“离枝”。

这是一种非常精贵的水果,一旦离开枝头,色泽、香气和味道会在短时间内迅速变质。

但它又是非常美味,宋代大文豪苏轼写道:“日啖荔枝三百颗,不辞长做岭南人”,以对其赞誉有加。

在唐朝时期,有一个著名的"荔枝使"杨太真(杨贵妃)的故事。为了满足杨贵妃对新鲜荔枝的喜爱,唐玄宗设立了专门的"荔枝驿",命人快马加鞭将岭南新鲜荔枝送至长安。这个历史典故不仅体现了古代物流系统的重要性,也启发我们思考现代物流优化问题。

现在假如我们是荔枝使,又该如何解决问题呢?

哈哈,快递小哥可真不好当啊~

下面本文将介绍如何使用A*(A-Star)算法来优化荔枝运输路径,实现从深圳到西安的最优路径规划。我们不仅要考虑路径的最短时间,还要处理可能出现的路径中断等突发情况。

问题描述

业务场景

  • 起点:深圳(主要荔枝产地)
  • 终点:西安(目的地)
  • 途经城市:广州、东莞、惠州、韶关、长沙、武汉、郑州、洛阳等
  • 目标:找到最短运输时间的路径
  • 附加要求:能够处理路径中断情况并重新规划路线

技术挑战

  1. 如何构建合适的城市网络模型
  2. 如何设计启发式函数以提高路径搜索效率
  3. 如何处理路径中断等异常情况
  4. 如何可视化运输路径以便直观展示

A*算法原理

算法介绍

A*算法是一种启发式搜索算法,它结合了Dijkstra算法和最佳优先搜索的优点。算法使用一个评估函数f(n)来决定搜索的方向:

f(n) = g(n) + h(n)

其中:

  • g(n):从起点到当前节点n的实际代价
  • h(n):从当前节点n到目标节点的估计代价(启发式函数)
  • f(n):总估计代价

启发式函数设计

在我们的荔枝运输问题中,启发式函数使用城市间的直线距离作为估计值。这是因为:

  1. 直线距离永远不会超过实际路径距离(满足可接受性)
  2. 计算简单,易于实现
  3. 能够较好地反映城市间的地理关系

代码实现

1. 城市网络模型

首先,我们需要构建城市之间的连接关系和运输时间:

# 城市之间的连接关系和运输时间(小时)

city\_graph = {

    '深圳': {'广州': 2, '东莞': 1, '惠州': 1.5},

    '广州': {'深圳': 2, '东莞': 1.5, '韶关': 2.5},

    '东莞': {'深圳': 1, '广州': 1.5, '惠州': 1.2},

    '惠州': {'深圳': 1.5, '东莞': 1.2, '武汉': 8},

    '韶关': {'广州': 2.5, '长沙': 4},

    '长沙': {'韶关': 4, '武汉': 2.5},

    '武汉': {'长沙': 2.5, '惠州': 8, '郑州': 4, '西安': 10},

    '郑州': {'武汉': 4, '洛阳': 1.5, '西安': 6},

    '洛阳': {'郑州': 1.5, '西安': 5},

    '西安': {'武汉': 10, '郑州': 6, '洛阳': 5}

}

2. 城市坐标系统

为了计算启发式函数,我们需要定义城市的相对坐标:

# 城市的相对坐标(用于启发式函数和可视化)

city\_coordinates = {

    # 路径上的城市

    '深圳': (0, 0),      # 起点

    '广州': (2, 2),

    '长沙': (4, 4),

    '武汉': (6, 6),

    '西安': (8, 4),      # 终点

    

    # 不在主路径上的城市

    '东莞': (1, -1),     # 向下偏移

    '惠州': (2, -2),     # 向下偏移

    '韶关': (3, 1),      # 向上偏移

    '郑州': (7, 8),      # 向上偏移

    '洛阳': (9, 7)       # 向右上偏移

}

3. A*算法核心实现

def heuristic(city1, city2):

    """

    启发式函数:计算两个城市之间的直线距离

    """

    x1, y1 = city\_coordinates[city1]

    x2, y2 = city\_coordinates[city2]

    return math.sqrt((x2 - x1) \*\* 2 + (y2 - y1) \*\* 2)



def astar\_search(graph, start, goal):

    """

    A\*算法实现

    """

    frontier = []  # 优先队列,存储待探索的节点

    heapq.heappush(frontier, (0, start))  # (优先级, 城市)

    

    came\_from = {start: None}  # 记录路径

    cost\_so\_far = {start: 0}   # 记录从起点到每个城市的实际代价

    

    while frontier:

        current\_cost, current\_city = heapq.heappop(frontier)

        

        # 到达目标城市

        if current\_city == goal:

            break

            

        # 探索相邻城市

        for next\_city, time in graph[current\_city].items():

            new\_cost = cost\_so\_far[current\_city] + time

            

            # 如果找到更好的路径或者是第一次访问这个城市

            if next\_city not in cost\_so\_far or new\_cost < cost\_so\_far[next\_city]:

                cost\_so\_far[next\_city] = new\_cost

                # f(n) = g(n) + h(n)

                priority = new\_cost + heuristic(next\_city, goal)

                heapq.heappush(frontier, (priority, next\_city))

                came\_from[next\_city] = current\_city

    

    # 重建路径

    if goal not in came\_from:

        return None, float('inf')

        

    path = []

    current\_city = goal

    while current\_city is not None:

        path.append(current\_city)

        current\_city = came\_from[current\_city]

    path.reverse()

    

    return path, cost\_so\_far[goal]

4. 路径可视化

def visualize\_path(graph, path):

    """

    使用matplotlib可视化运输路径

    """

    plt.figure(figsize=(12, 8))

    

    # 绘制所有城市点

    for city, (x, y) in city\_coordinates.items():

        if city in path:

            plt.plot(x, y, 'ro', markersize=10, label=city if city == path[0] or city == path[-1] else "")

        else:

            plt.plot(x, y, 'bo', markersize=8)

        plt.annotate(city, (x, y), xytext=(5, 5), textcoords='offset points')

    

    # 绘制所有道路连接

    for city1 in graph:

        for city2 in graph[city1]:

            x1, y1 = city\_coordinates[city1]

            x2, y2 = city\_coordinates[city2]

            plt.plot([x1, x2], [y1, y2], 'gray', linestyle='--', alpha=0.3)

    

    # 绘制最优路径

    for i in range(len(path) - 1):

        city1 = path[i]

        city2 = path[i + 1]

        x1, y1 = city\_coordinates[city1]

        x2, y2 = city\_coordinates[city2]

        plt.plot([x1, x2], [y1, y2], 'r-', linewidth=2)

    

    plt.title('荔枝运输最优路径')

    plt.legend()

    plt.grid(True)

    plt.show()

5. 路径中断处理

def remove\_edge(graph, city1, city2):

    """

    从图中移除两个城市之间的连接

    """

    new\_graph = {city: neighbors.copy() for city, neighbors in graph.items()}

    

    if city2 in new\_graph[city1]:

        del new\_graph[city1][city2]

    if city1 in new\_graph[city2]:

        del new\_graph[city2][city1]

    

    return new\_graph

功能演示

1. 基本路径规划

start\_city = '深圳'

end\_city = '西安'

optimal\_path, total\_cost = astar\_search(city\_graph, start\_city, end\_city)



print("最优路径:", " → ".join(optimal\_path))

print(f"总运输时间: {total\_cost}小时")

输出结果:

请在此添加图片描述

由此可见:

最优路径: 深圳 → 广州 → 长沙 → 武汉 → 西安

总运输时间: 20.0小时

显示绘图如下:

请在此添加图片描述

2. 处理路径中断

当深圳到广州的直接路径中断时:

# 断开深圳和广州之间的连接

modified\_graph = remove\_edge(city\_graph, '深圳', '广州')

optimal\_path, total\_cost = astar\_search(modified\_graph, start\_city, end\_city)



print("路径中断后的最优路径:", " → ".join(optimal\_path))

print(f"总运输时间: {total\_cost}小时")

输出结果:

请在此添加图片描述

可以看到,

路径中断后的最优路径: 深圳 → 东莞 → 惠州 → 武汉 → 西安

总运输时间: 20.2小时

这条路径上已经没有广州这个节点了,变动后的路线图如下所示:

请在此添加图片描述

让我们分析一下结果:

  1. 成功断开了深圳和广州之间的连接
  2. 程序正确处理了城市编号输入(1代表深圳,10代表西安)
  3. A*算法成功找到了一条替代路径:

原始路径是:深圳 → 广州 → 长沙 → 武汉 → 西安 (20.0小时)

新路径是:深圳 → 东莞 → 惠州 → 武汉 → 西安 (20.2小时)

  1. 新路径的总时间只比原路径多了0.2小时,这是一个很好的替代方案

优化过程——启发式函数改进

最初我们使用曼哈顿距离作为启发式函数:

def heuristic\_manhattan(city1, city2):

    x1, y1 = city\_coordinates[city1]

    x2, y2 = city\_coordinates[city2]

    return abs(x2 - x1) + abs(y2 - y1)

后来改用欧几里得距离,因为:

  • 更符合实际地理距离
  • 提供了更准确的估计
  • 保证了解的最优性

3. 用户交互优化

添加了以下功能:

  • 支持通过城市编号或名称选择
  • 提供清晰的城市列表
  • 增加了输入验证和错误处理

实验结果分析

1. 基本路径对比

| 路径方案 | 路线 | 总时间 |

|---------|------|--------|

| 最优路径 | 深圳→广州→长沙→武汉→西安 | 20.0小时 |

| 次优路径 | 深圳→东莞→惠州→武汉→西安 | 20.2小时 |

| 替代路径 | 深圳→广州→长沙→武汉→郑州→西安 | 23.0小时 |

2. 路径中断情况分析

我们测试了多种路径中断场景:

  1. 深圳-广州中断
  2. 系统自动选择通过东莞-惠州的替代路线
  3. 仅增加0.2小时运输时间
  4. 武汉-西安中断
  5. 系统选择通过郑州或洛阳的北线路径
  6. 运输时间增加约3-4小时
  7. 多路段同时中断
  8. 系统能够找到可行的替代路径
  9. 保证了运输网络的鲁棒性

总结与展望

本次开发实践的主要成果

  1. 实现了基于A*算法的智能路径规划系统
  2. 支持路径中断的动态调整
  3. 提供了直观的路径可视化
  4. 优化了用户交互体验

可能的改进方向

方向一:考虑更多现实因素

  • 交通拥堵情况
  • 天气影响
  • 时间窗口限制

方向二:算法优化

  • 实现多目标优化
  • 添加实时路况更新
  • 支持批量路径规划

方向三:系统扩展

  • 接入实时地图数据
  • 添加路况预警功能
  • 支持多种运输方式组合
  • 开发移动应用程序

历史与现代的结合

从唐朝的"荔枝驿"到现代的智能物流系统,人类对高效运输的追求从未停止。通过A*算法这样的现代计算技术,我们能够更好地解决古老的物流问题。杨贵妃的荔枝,如今可以通过最优路径,以最短的时间从岭南送达长安,保持最佳的新鲜度。

这个题目不仅是对算法的实践,也是对历史与现代技术结合的一次有趣探索。它提醒我们,无论是古代还是现代,高效的物流系统都是社会发展的重要基础。

对于这个问题本身来说,如果加入考虑更多极端条件,比如运输的人力物力调度,在什么位置由谁接力续运的接力赛等等,那么问题就会转换成最大最小流一类的新算法问题…

哈哈哈,是不是 更加复杂呢?

贵妃的荔枝,究竟是怎么运,这是一个有趣的算法哲学问题。欢迎大家共同探讨~!

附录:完整代码

import math

import heapq

import matplotlib.pyplot as plt



# 城市之间的连接关系和运输时间(小时)

city\_graph = {

    '深圳': {'广州': 2, '东莞': 1, '惠州': 1.5},

    '广州': {'深圳': 2, '东莞': 1.5, '韶关': 2.5},

    '东莞': {'深圳': 1, '广州': 1.5, '惠州': 1.2},

    '惠州': {'深圳': 1.5, '东莞': 1.2, '武汉': 8},

    '韶关': {'广州': 2.5, '长沙': 4},

    '长沙': {'韶关': 4, '武汉': 2.5},

    '武汉': {'长沙': 2.5, '惠州': 8, '郑州': 4, '西安': 10},

    '郑州': {'武汉': 4, '洛阳': 1.5, '西安': 6},

    '洛阳': {'郑州': 1.5, '西安': 5},

    '西安': {'武汉': 10, '郑州': 6, '洛阳': 5}

}



# 城市的相对坐标(用于启发式函数和可视化)

city\_coordinates = {

    # 路径上的城市

    '深圳': (0, 0),      # 起点

    '广州': (2, 2),

    '长沙': (4, 4),

    '武汉': (6, 6),

    '西安': (8, 4),      # 终点

    

    # 不在主路径上的城市

    '东莞': (1, -1),     # 向下偏移

    '惠州': (2, -2),     # 向下偏移

    '韶关': (3, 1),      # 向上偏移

    '郑州': (7, 8),      # 向上偏移

    '洛阳': (9, 7)       # 向右上偏移

}



def heuristic(city1, city2):

    """

    启发式函数:计算两个城市之间的直线距离

    """

    x1, y1 = city\_coordinates[city1]

    x2, y2 = city\_coordinates[city2]

    return math.sqrt((x2 - x1) \*\* 2 + (y2 - y1) \*\* 2)



def astar\_search(graph, start, goal):

    """

    A\*算法实现

    """

    frontier = []  # 优先队列,存储待探索的节点

    heapq.heappush(frontier, (0, start))  # (优先级, 城市)

    

    came\_from = {start: None}  # 记录路径

    cost\_so\_far = {start: 0}   # 记录从起点到每个城市的实际代价

    

    while frontier:

        current\_cost, current\_city = heapq.heappop(frontier)

        

        # 到达目标城市

        if current\_city == goal:

            break

            

        # 探索相邻城市

        for next\_city, time in graph[current\_city].items():

            new\_cost = cost\_so\_far[current\_city] + time

            

            # 如果找到更好的路径或者是第一次访问这个城市

            if next\_city not in cost\_so\_far or new\_cost < cost\_so\_far[next\_city]:

                cost\_so\_far[next\_city] = new\_cost

                # f(n) = g(n) + h(n)

                priority = new\_cost + heuristic(next\_city, goal)

                heapq.heappush(frontier, (priority, next\_city))

                came\_from[next\_city] = current\_city

    

    # 重建路径

    if goal not in came\_from:

        return None, float('inf')

        

    path = []

    current\_city = goal

    while current\_city is not None:

        path.append(current\_city)

        current\_city = came\_from[current\_city]

    path.reverse()

    

    return path, cost\_so\_far[goal]



def visualize\_path(graph, path):

    """

    使用matplotlib可视化运输路径

    """

    plt.figure(figsize=(12, 8))

    

    # 绘制所有城市点

    for city, (x, y) in city\_coordinates.items():

        if city in path:

            plt.plot(x, y, 'ro', markersize=10, label=city if city == path[0] or city == path[-1] else "")

        else:

            plt.plot(x, y, 'bo', markersize=8)

        plt.annotate(city, (x, y), xytext=(5, 5), textcoords='offset points')

    

    # 绘制所有道路连接

    for city1 in graph:

        for city2 in graph[city1]:

            x1, y1 = city\_coordinates[city1]

            x2, y2 = city\_coordinates[city2]

            plt.plot([x1, x2], [y1, y2], 'gray', linestyle='--', alpha=0.3)

    

    # 绘制最优路径

    for i in range(len(path) - 1):

        city1 = path[i]

        city2 = path[i + 1]

        x1, y1 = city\_coordinates[city1]

        x2, y2 = city\_coordinates[city2]

        plt.plot([x1, x2], [y1, y2], 'r-', linewidth=2)

    

    plt.title('荔枝运输最优路径')

    plt.legend()

    plt.grid(True)

    plt.show()



def remove\_edge(graph, city1, city2):

    """

    从图中移除两个城市之间的连接

    """

    new\_graph = {city: neighbors.copy() for city, neighbors in graph.items()}

    

    if city2 in new\_graph[city1]:

        del new\_graph[city1][city2]

    if city1 in new\_graph[city2]:

        del new\_graph[city2][city1]

    

    return new\_graph



def get\_city\_by\_input(cities, prompt, default=None):

    """

    获取用户输入的城市,支持通过编号或名称选择

    """

    user\_input = input(prompt)

    

    # 如果用户没有输入,使用默认值

    if not user\_input and default:

        return default

    

    # 尝试将输入解析为数字(城市编号)

    try:

        idx = int(user\_input) - 1

        if 0 <= idx < len(cities):

            return cities[idx]

    except ValueError:

        # 如果输入不是数字,检查是否是城市名称

        if user\_input in cities:

            return user\_input

    

    # 如果输入既不是有效的编号也不是有效的城市名称,返回None

    return None



def main():

    print("荔枝运输路径优化系统 (A\*算法)")

    print("=" \* 50)

    

    # 创建图的副本,以便可以修改

    current\_graph = {city: neighbors.copy() for city, neighbors in city\_graph.items()}

    cities = list(current\_graph.keys())

    

    # 询问用户是否要断开某条路径

    disconnect = input("是否模拟城市之间断联? (y/n): ").lower() == 'y'

    

    if disconnect:

        # 显示所有城市

        print("\n可选城市:")

        for i, city in enumerate(cities):

            print(f"{i+1}. {city}")

        

        # 获取用户输入

        try:

            city1\_idx = int(input("\n请选择第一个城市编号: ")) - 1

            city2\_idx = int(input("请选择第二个城市编号: ")) - 1

            

            if (city1\_idx < 0 or city1\_idx >= len(cities) or 

                city2\_idx < 0 or city2\_idx >= len(cities)):

                print("无效的城市编号!")

                return

            

            city1 = cities[city1\_idx]

            city2 = cities[city2\_idx]

            

            # 检查两个城市是否相邻

            if city2 not in current\_graph[city1] and city1 not in current\_graph[city2]:

                print(f"{city1}{city2}之间没有直接连接!")

                return

            

            # 断开连接

            current\_graph = remove\_edge(current\_graph, city1, city2)

            print(f"\n已断开 {city1}{city2} 之间的连接")

        except ValueError:

            print("请输入有效的数字!")

            return

    

    # 显示所有城市

    print("\n可选城市:")

    for i, city in enumerate(cities):

        print(f"{i+1}. {city}")

    

    # 获取起点和终点

    print("\n可以输入城市名称或编号")

    start\_city = get\_city\_by\_input(cities, "请输入起点城市 (默认: 深圳): ", '深圳')

    end\_city = get\_city\_by\_input(cities, "请输入终点城市 (默认: 西安): ", '西安')

    

    if not start\_city or not end\_city:

        print("无效的城市选择!")

        return

    

    print(f"\n寻找从 {start\_city}{end\_city} 的最优路径...")

    

    # 使用A\*算法寻找最短路径

    optimal\_path, total\_cost = astar\_search(current\_graph, start\_city, end\_city)

    

    if optimal\_path:

        print("\n最优路径:", " → ".join(optimal\_path))

        print(f"总运输时间: {total\_cost}小时")

        

        # 打印详细路径信息

        print("\n详细路径信息:")

        print("-" \* 50)

        for i in range(len(optimal\_path) - 1):

            from\_city = optimal\_path[i]

            to\_city = optimal\_path[i + 1]

            time = current\_graph[from\_city][to\_city]

            print(f"{from\_city}{to\_city}: {time}小时")

        print("-" \* 50)

        print(f"总计: {total\_cost}小时")

        

        # 可视化路径

        visualize\_path(current\_graph, optimal\_path)

    else:

        print(f"无法找到从 {start\_city}{end\_city} 的路径!")



if \_\_name\_\_ == "\_\_main\_\_":

    main()

网站公告

今日签到

点亮在社区的每一天
去签到