
上周末,几个朋友计划在成都进行一场“特种兵式”一日游,他们兴致勃勃地列出了 8 个必去打卡点,然后开始面对那个永恒的难题:我们应该按什么顺序走,才能最快、最省力地玩遍所有地方?
朋友 A 是个行动派,他打开地图,凭感觉连线,半小时后拿出了一套方案。
朋友 B 则以严谨著称,他花了半天时间,反复对比、推敲,最终给出了一个他认为“绝对不可能被超越”的优化路线。
看着他们争执不下,我笑了笑,默默打开了我的电脑。
“或许,我们可以让代码来做个裁判。”
这篇文章,就是那场“人与机器”对决的实录。结果,绝对出乎你的意料。
我们选取八个热门地点作为目的地:西村大院,水井坊博物馆,太古里,当代美术馆,来福士广场,宽窄巷子,武侯祠横街,九眼桥,他们之间没有顺序限制,但是起点必须从成都俊发星雅俊园出发,终点为**全季酒店(成都武侯祠店)**。
为了看得更清楚,我绘制了 10 个点在地图上的分布图:

10 个点,点击阅读原文可以查看交互地图
我的目标很简单:
我特意找了两个朋友来手动规划路线,屏幕前的你也可以在上图中用箭头标出你的路线。
而我,我决定将这个规划难题交给算法来处理。路线规则则基于高德地图 api 实时规划,时间采用工作日上午,不会碰到早高峰。
接下来,就是我利用 Python 的两种不同算法,对比我们人类的路径规划能力和计算机之间的差距。
首先登场的是最广为人知的“最近邻算法”(Nearest Neighbor)。它的逻辑非常简单粗暴:从起点出发,永远选择离当前位置最近的下一个点,直到走完所有点。
这基本就是我们大多数人凭直觉规划路线时的思维方式。结果呢?

最近邻算法,点击阅读原文可以查看交互地图
最近邻算法路线:总耗时 211 分钟以上。
它在前期看似高效,但由于缺乏全局视野,浪费了大量时间在市中心与周边几个点的通勤上。
=== 最近邻算法 路线详情 ===
总驾车时间: 211.8分钟 (3.5小时)
详细路线:
1. 成都俊发星雅俊园 → 成都来福士广场 (31.9分钟)
2. 成都来福士广场 → 成都九眼桥 (14.0分钟)
3. 成都九眼桥 → 成都水井坊博物馆 (12.3分钟)
4. 成都水井坊博物馆 → 成都太古里 (11.8分钟)
5. 成都太古里 → 成都宽窄巷子 (22.4分钟)
6. 成都宽窄巷子 → 成都西村大院 (19.9分钟)
7. 成都西村大院 → 成都市武侯祠横街 (32.3分钟)
8. 成都市武侯祠横街 → 成都当代美术馆 (29.2分钟)
9. 成都当代美术馆 → 全季酒店(成都武侯祠店) (37.9分钟)
10. 全季酒店(成都武侯祠店) (终点)
结论:无脑“抄近道”,死路一条。
简单算法不行,我们得上更强大的“遗传算法”(Genetic Algorithm)。
你不需要理解它的复杂代码,只需要知道它的思想源于生物学的“优胜劣汰”。它会这样做:
最终,那个在残酷竞争中存活下来的“天选之子”,就是我们要找的最优路线。
我设定好参数,按下了执行键。10 秒后,结果跃然屏上。
相比于最近邻算法的211分钟,这是一个总耗时181.9分钟的更优解:

遗传算法的解法,点击阅读原文可以查看交互地图
=== 遗传算法 路线详情 ===
总驾车时间: 181.9分钟 (3.0小时)
详细路线:
1. 成都俊发星雅俊园 → 成都当代美术馆 (39.6分钟)
2. 成都当代美术馆 → 成都来福士广场 (23.9分钟)
3. 成都来福士广场 → 成都九眼桥 (14.0分钟)
4. 成都九眼桥 → 成都水井坊博物馆 (12.3分钟)
5. 成都水井坊博物馆 → 成都太古里 (11.8分钟)
6. 成都太古里 → 成都西村大院 (29.0分钟)
7. 成都西村大院 → 成都宽窄巷子 (22.3分钟)
8. 成都宽窄巷子 → 成都市武侯祠横街 (20.6分钟)
9. 成都市武侯祠横街 → 全季酒店(成都武侯祠店) (8.4分钟)
10. 全季酒店(成都武侯祠店) (终点)
看完机器的路径,我们来看人类是如何规划的:
想必很多人心里都有一个答案,大致分为两类:
第一位朋友 A,就给出了比最近邻算法更优的方案,我称之为“先清边,后包围”:

朋友A的答案。
如果我们模拟一个人类视角来规划路线,可能就只能考虑点之间的距离,因为我们规划路线的时候,不可能去手动查询每一个起点和终点。 对于 5 个景点,这意味着
5 * 4 = 20次查询。如果要去 10 个景点,查询次数将暴增到10 * 9 = 90次!这个过程不仅耗时,而且极度枯燥,不可能会先完全查询再规划,我们只会根据直线距离的远近以及大致的车程来计算。
所以他的思路大致是这样的:
这背后的行车路线是这样的:

朋友A
朋友A的总行程时间:192.3 分钟(3.20 小时)
正在生成路线地图...
绘制真实路径: 成都俊发星雅俊园 → 成都当代美术馆 (231个坐标点)
绘制真实路径: 成都当代美术馆 → 成都西村大院 (259个坐标点)
绘制真实路径: 成都西村大院 → 成都宽窄巷子 (122个坐标点)
绘制真实路径: 成都宽窄巷子 → 成都太古里 (93个坐标点)
绘制真实路径: 成都太古里 → 成都水井坊博物馆 (85个坐标点)
绘制真实路径: 成都水井坊博物馆 → 成都九眼桥 (71个坐标点)
绘制真实路径: 成都九眼桥 → 成都来福士广场 (134个坐标点)
绘制真实路径: 成都来福士广场 → 成都市武侯祠横街 (113个坐标点)
绘制真实路径: 成都市武侯祠横街 → 全季酒店(成都武侯祠店) (59个坐标点)
显然朋友 A 的路线也有瑕疵,尤其是在“成都当代美术馆”到“西村大院”之间绕了远路。即便如此,他花费的时间 192 分钟 也比最近邻算法的 211 分钟,快了近 10 分钟!
我们再来看看另一位朋友 B 是怎么想的:她也采用了“先清边,后包围”的策略,但在细节处理上更为老到:抵达“当代美术馆”后,她没有像朋友 A 那样长途奔袭到西村,而是选择北上先进入市中心,以环状路线将中心景点串联起来,最后才回到终点酒店。

朋友B
这位朋友的空间直觉与视觉处理能力非常强。她的大脑能够瞬间处理图形信息,找出一条“看起来最顺”的路径。而且,她还可能运用了“太古里和水井坊很近,可以打包游览”这类普通算法不知道的“隐形知识”。
按照她思路规划出的真实行车路线如下:
总行程时间:184.7 分钟
成都俊发星雅俊园 → 成都当代美术馆: 39.6 分钟
成都当代美术馆 → 成都来福士广场: 23.9 分钟
成都来福士广场 → 成都九眼桥: 14.0 分钟
成都九眼桥 → 成都水井坊博物馆: 12.3 分钟
成都水井坊博物馆 → 成都太古里: 11.8 分钟
成都太古里 → 成都宽窄巷子: 22.4 分钟
成都宽窄巷子 → 成都西村大院: 19.9 分钟
成都西村大院 → 成都市武侯祠横街: 32.3 分钟
成都市武侯祠横街 → 全季酒店(成都武侯祠店): 8.4 分钟
朋友 B 背后的行车路线是:

朋友B背后的行车路线
看完了人类选手的精彩表现和算法的冷静计算,现在是时候揭晓最终的排名了。
现在,所有选手的最终成绩都已出炉,让我们把它们放在一起看看:
211.8 分钟192.3 分钟184.7 分钟181.9 分钟结果揭晓:遗传算法以2.8分钟的微弱优势,险胜最强人类!
这个结果,远比“机器碾压人类”的剧本要有趣得多。但我们不禁要问:这决定胜负的区区几分钟,究竟差在了哪里?
乍一看,两条路线的宏观策略惊人地一致:
从起点出发,先向南解决最远的当代美术馆,然后北上进入市中心,以环线方式清扫中部景点,最后回到武侯祠附近的终点。这雄辩地证明了朋友B的“先清边,后包围”策略是完全正确的。
真正的分歧点,仅在于对宽窄巷子和西村大院这两个景点的访问顺序上。 一个微小的调整,造就了截然不同的结果。
遗传算法(冠军)左图 vs 朋友B(亚军)右图:

遗传算法(冠军)左图 vs 朋友B(亚军)右图
朋友B(亚军)的顺序: ... → 太古里 → 宽窄巷子 → 西村大院 → ...
遗传算法(冠军)的顺序**: ... → 太古里 → 西村大院 → 宽窄巷子 → ...
太古里完成一次长距离的“跳跃”,先去更西边的西村大院,然后再“折返”到东边的宽窄巷子。为什么这个看似“绕路”的选择反而更快?我们来分解这两步的耗时:
太古里 → 宽窄巷子 (22.4分钟)宽窄巷子 → 西村大院 (19.9分钟)太古里 → 西村大院 (29.0分钟)西村大院 → 宽窄巷子 (22.3分钟)看到这里,你可能会疑惑,算法的这两步明明多花了9分钟,它怎么赢的?
答案在下一步!
西村大院出发后,朋友B的下一站是武侯祠横街,耗时32.3分钟。宽窄巷子出发后,算法的下一站也是武侯祠横街,耗时20.6分钟。在这里,算法凭空多抢回了将近12分钟!
结论已经非常清晰:
算法用前两步多付出的9分钟作为代价,换取了在关键的下一步上节省12分钟的巨大优势。这是一次经典的 “牺牲局部最优,换取全局胜利” 。它通过一次反直觉长途奔袭,将自己送到了一个更有利的出发点,从而在后续路程中获得了回报。
这正是人类思维的盲点。我们倾向于选择“步步为营”的最优解,而算法却能洞察全局,完成一次我们绝不会考虑的、跨越式的“战略投资”。
那么,既然最强的算法也只是险胜顶尖的人类,我们为什么还需要它?
因为,我们大多数人在规划路线时,都只是“朋友 A”的水平,甚至还不如。我们没有天赋,也没有精力去反复推演那40,320种可能性。
而算法的真正价值正在于此:它将朋友 B 这种万里挑一的“大师级”规划能力,变成了一种人人都能在 10 秒内稳定拥有的“基础服务”。
算法的存在,不是为了和最强的人类一较高下,而是为了补足我们普通人的短板,让我们每个人都能轻松拥有专属于自己的“最优解”。
回到最初的问题:花半天规划的旅行路线,为什么还不如 Python 跑 10 秒的结果?
答案已经清晰:
因为那 10 秒的背后,站着一位永远不知疲倦、能瞬间看透全局的规划大师。即便最顶尖的人类也可能出现微小偏差,但它总能稳定地交付那个无限接近完美的答案。
不过,这次对决是在路况良好的周一上午进行的。一个更有趣的问题浮现在我脑海里:
如果把这两条路线,同时抛入周一早上 8 点的“地狱级”早高峰里,战局又会如何?
人类积累的“路感”和经验,还能否对抗由无数数据点汇集而成的“堵车洪流”?在那样的极限压力下,算法又是否能找到一条我们人类无法想象的“求生之路”?
我把这个问题,留给屏幕前的你。
如果你对这个决感兴趣,不妨自己动手尝试一下。源代码可以访问 Github:旅行规划问题[1]获取。
需要强调的是,这次演示是一个在高度理想化条件下进行的实验。为了聚焦于路线规划算法本身,我忽略了大量现实世界中的复杂因素,例如:
因此,这个脚本目前还只是一个有趣的“玩具”,而非一个成熟的旅行规划工具。
这次简单的尝试也让我看到了这类工具的巨大潜力。基于现有框架,我们可以继续探索,让它变得更强大、更“人性化”。
总而言之,算法或许不能完全替代我们旅行中的随性与惊喜,但它无疑可以成为一个强大的助手,帮助我们处理掉规划中最繁琐和重复的部分,让我们能把更多宝贵的时间,留给真正的风景。
目前是在“重复造轮子”。我手动实现了最近邻和遗传算法,这对于学习和理解原理非常好。但工业界有现成的、经过高度优化的库(如OR-Tools, NetworkX),它们通常比我们手写的版本更快、更稳定、结果也更好。
欢迎在评论区里疯狂吐槽你在 ArcGIS、python 代码里遇到的烦心事,或者聊聊你对这个系列、对哪个技术方向最感兴趣。你们的反馈会直接影响我后续内容的侧重点!
参考资料
[1]
旅行规划问题: https://github.com/renhai-lab/Urban-Spatial-Data-Analysis-Notebook/tree/main/4-%E7%A9%BA%E9%97%B4%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/%E5%85%B6%E4%BB%96%E6%A1%88%E4%BE%8B/%E6%97%85%E8%A1%8C%E8%A7%84%E5%88%92%E9%97%AE%E9%A2%98
[2]
个人网站: https://www.renhai.online/
[3]
我的知乎: https://www.zhihu.com/people/Ing_ideas
[4]
我的博客: https://www.renhai.online/blog
[5]
我的 GITHUB: https://github.com/renhai-lab
[6]
我的 GITEE: https://gitee.com/renhai-lab
[7]
RSS: https://www.renhai.online/rss.xml