当前位置: 首页 > news >正文

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

请在此添加图片描述
原文首发请访问: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 = goalwhile 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 mathimport heapqimport 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 = goalwhile 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\_graphdef 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) - 1if 0 <= idx < len(cities):return cities[idx]except ValueError:# 如果输入不是数字,检查是否是城市名称if user\_input in cities:return user\_input# 如果输入既不是有效的编号也不是有效的城市名称,返回Nonereturn Nonedef 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请选择第一个城市编号: ")) - 1city2\_idx = int(input("请选择第二个城市编号: ")) - 1if (city1\_idx < 0 or city1\_idx >= len(cities) or city2\_idx < 0 or city2\_idx >= len(cities)):print("无效的城市编号!")returncity1 = 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("无效的城市选择!")returnprint(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()

相关文章:

  • Nginx+tomcat集群
  • AI Gateway 介绍
  • 【Node】最佳Node.js后端开发模板推荐
  • 分组交换比报文交换的传输时延更低
  • AWS-SAA 第二部份:安全性和权限管理
  • Docker学习笔记:DockerFile
  • 服务发现与动态负载均衡的结合
  • Typora文档另存与图片迁移的一种思路
  • nginx代理与tomcat动静分离
  • app专项测试命令如何写?
  • Zephyr 电源管理机制深度解析:从 Tickless Idle 到平台 Suspend 实践
  • C++11的一些特性
  • JavaEE:使用JMeter进行接口并发测试
  • docker执行yum报错Could not resolve host: mirrorlist.centos.org
  • 旧物回收小程序开发:开启绿色生活新方式
  • 使用Vue3开发Electorn桌面应用
  • 车载电子电器架构 --- 电子电气架构设计方案
  • 容器通信技术与数据持久化
  • Solidity内部合约创建全解析:解锁Web3开发新姿势
  • 4.1 FFmpeg编译选项配置
  • 湖南pc网站建设费用/网络公司网站模板
  • 王府井网上商城官网/关键词排名快照优化
  • 红色网站建设的比较好的高校/北京百度推广开户
  • 网站网页模板/网站运营推广方案
  • 上海网站建设流程/长沙百度推广排名优化
  • 做时时网站要多少钱/电商网站如何避免客户信息泄露