二刷九寨沟后,我把它「世界最优」的景区调度算法做成了可玩的网页沙盘

📁 项目作品

九寨引力场 GravityField:把牛顿万有引力搬进景区调度

引子:一个二刷游客的好奇心

第一次去九寨沟是走马观花,眼里只有五花海的水和长海的雪。第二次去,人还是那么多,我却开始犯职业病——为什么这里几乎不用排队?

一天几万人涌进一条沟,换成别的景区,体验通常是:索道排两小时、观光车等五趟、下山全靠腿。但在九寨沟,大巴像水一样流动:你在站点站定,车就来了;你逛完一个海子,总有车把你送往下一个。全程没有人喊号、没有人维持上车秩序,一切都”恰好”发生。

回来查资料才知道,这背后是四川大学商学院和九寨沟管理局合作研究的「旅游高峰期景区游客引力分流调度模型」,公开报道里有一个非常夸张的数字:**大巴空驶率从 35% 降到了 8%**。在运筹学圈子里,这套东西经常被拿来当景区调度的标杆案例。

它到底优秀在哪?我干脆把算法复刻成了一个网页沙盘,你可以直接在下文里玩,也可以全屏打开。

先说清楚:九寨沟调度到底难在哪

不懂运筹学没关系,先看地形。九寨沟的路线是一个 Y 字形:

  • 所有人从沟口进出,只有一个大门;
  • 坐车往里走,到诺日朗中心站分成两条支沟:右上日则沟(五花海、珍珠滩、原始森林),左上则查洼沟(五彩池、长海);
  • 每个景点像树枝上的叶子,车只能沿树枝开,不能飞过去。

这就形成了一个经典的难题:几万人同时在一个”Y 字树”上移动,车只有几十辆,怎么派才能让所有人不等太久?

更麻烦的是真实运营的硬约束,沙盘里我都做了进去:

  • 14:00 停止检票,上行班车逐步收尾——入园人数是有闸机的,可控;
  • 16:30 起支线收车,全部大巴转入下行清运模式,只下不上;
  • 长海 16:30 末班、原始森林 18:00 末班、诺日朗回沟口 19:00 末班——末班车的约束是刚性的,不能因为调度把游客留在山里。

也就是说,这不是”高峰派车多、平峰派车少”这么简单,而是一整天不断变化的动态配兵问题。

算法的答案:把「哪里最挤」和「谁能救」都当成质量

传统调度最容易想到的做法是就近调度:哪个景点有人等车,就派离它最近的大巴去。直觉很对,效果一般——最近的车可能只有 3 个空座,去了也接不走几个人;或者它自己还带着满车游客,绕路反而添堵。

九寨沟的引力模型换了一个思路:把牛顿的万有引力公式直接搬过来。

$$F = \frac{D \cdot C}{d^2}$$

别被公式吓到,每一项都是大白话:

符号 含义 人话
D 需求强度 这个景点有多挤(候车人数 + 负荷率)
C 空座数 这辆大巴能救多少人
d 车程 大巴开过去要多久

引力 F 最大的大巴,出发。 也就是说,算法同时权衡了三件事:越急的地方优先、越空的车优先、越近的车优先——而不是只看距离。

整个流程每 30 秒跑一遍:

  1. 测需求:用门票预约锁总量,闸机和摄像头推算每个景点有多少人,算出时空负荷率 R = N·t/c(人多、玩得久、容量小 → R 高);
  2. 排优先级:候车超过阈值的景点成为”需求景点”,按 R 从高到低排序——最挤的先挑车;
  3. 算引力:对每辆空闲大巴算一遍 F,挑 F 最大的派出去;
  4. 做匹配:大巴开到需求景点,沿途站点随到随上(这就是公交化循环,不是点对点专车);
  5. 再迭代:30 秒后所有人位置都变了,全部重算。

分流在游客无感中完成——这就是”优秀算法”的终极形态:你根本感觉不到它的存在。

沙盘上手:亲手跑一天九寨沟

下面就是沙盘本体,点开就能玩(手机上建议点”全屏打开”获得完整布局):

手机上建议 新窗口打开全屏体验

界面速览:左边是 Y 字形的实时地图(圆点是景点、方块是大巴、虚线是”引力连线”),右边是指挥面板,可以看实时指标、切调度模式、调客流强度、调车队规模。

推荐三个实验,小白也能玩出感觉:

实验一:默认跑一天,看结算。 什么都不动,看着大巴穿梭。到 19:00 末班收车会弹出全天结算——进园人数、平均候车、空驶率。默认的引力调度下,空驶率通常能压到 15% 左右。

实验二:切换「就近调度」对比。 点面板里的模式切换,再跑一天。你会发现就近调度在客流小的时候没问题,但午高峰时热门景点(五花海、五彩池)的候车时间会明显变长——只看距离的直觉,在拥挤场景下会失灵。

实验三:上强度,压车队。 把客流强度拉到「黄金周」,大巴数量砍到 18 辆,再点个「突发拥堵 · 五花海」。然后分别用两种模式跑——引力调度会”拆东墙补西墙”地把车从冷点抽去救热点,而就近调度往往顾此失彼。这就是那个 35% → 8% 的空驶率差距在沙盘里的缩影。

另外沙盘还支持 URL 参数直接快进:/lab/jiuzhaigou/?ff=230&surge=1 会快进到晚高峰 11:50 并注入五花海突发拥堵,适合直接演示最热闹的调度场面。

我的一些思考

为什么我说它是”最优秀”的那一档? 调度算法圈里有很经典的一句话:All models are wrong, but some are useful. 九寨沟这个模型的”有用”体现在两个细节:

一是它没有追求最优解。精确求解这种规模的动态调度问题,算力上不可行,等算出来黄花菜都凉了。引力模型是一个 O(车数 × 需求点数) 的贪心近似,几毫秒就能出结果——快,才配得上每 30 秒重算一次的节奏。工程上的优秀从来不等于数学上的最优。

二是它尊重了真实世界的全部约束。14:00 截流、16:30 收支线、末班车刚性——这些规则没有被算法”优化掉”,而是被算法当作边界条件老老实实遵守。很多纸面漂亮的调度方案死在”没考虑末班车”,这个模型没有。

二刷时我特意观察过诺日朗中心站:没有调度员拿着喇叭喊,没有人工举牌,大巴一辆接一辆地进出,像呼吸一样自然。当时只觉得流畅,现在复刻完算法再回想——那种流畅本身就是调度系统在呼吸。

最后,这个沙盘是纯前端的静态页面(4 个文件、零依赖),已经挂在本站的 /lab/jiuzhaigou/ 下,导航栏的「Lab」可以直接进入。源码就在博客仓库里,欢迎拿去玩、拿去改。