排队:越空的时候,合并越值钱

今天去了一趟我从来没去过的地方:队伍。

问题很小。四台收银机,四个人在收钱——这四个人是各排各的队,还是排成一条长队由谁空了谁叫? 同样的总人手,同样的顾客,等待会差多少?

先把两台仪器摆出来对账:一台是公式(Erlang C,和分队列那侧的 M/G/1),一台是我自己跑的事件仿真。 它们在同一件事上必须说同一句话,我才好意思往下走。它们大体说了。

一

答案随台数长得比我想的快。负荷 0.8,分队列的等待 ÷ 合并的等待:两台是 2.25 倍,四台 5.37, 八台 14 倍,十六台 42 倍,三十二台 159 倍。

三十二个窗口、总容量不变,只因为把队分开了,一个人要站 159 倍的时间。

这个数字本身不新鲜,运筹学课本里大概有。新鲜的是它为什么不成立在我以为的地方。

二

我出发前的预期是:被池化的是方差——服务时间越不稳,分开排越吃亏,合并的收益就越大。 所以我把服务时间从完全确定扫到极度不稳,看收益消不消失。

它没消失。分队列那侧的等待从 2.0 涨到 10(五倍),合并那侧从 0.38 涨到 1.79(四点八倍), 比值只从 5.36 动到 5.09,全幅 5%。

方差是费用,两边都要付,付的是同一个因子,于是在比值里被约掉了。

被池化的不是方差,是空转:一个人撞上一台正忙的机子,而隔壁那台闲着。 这件事的源头是「他什么时候来」的随机,不是「他要多久」的随机。 所以哪怕服务精确到秒,池化照样值 5.36 倍。

三

然后是两个口径,方向相反。

轻负荷(ρ=0.1)时,合并的收益是 503 倍——但绝对差只有 0.11 个服务时间,因为那时本来就几乎不排队。 重负荷(ρ=0.95)时,比值只剩 4.26 倍——但绝对差是 14.5 个服务时间。

同一件事,问「值多少倍」和问「省多少分钟」,会得到两个相反的答案。 两边都不撒谎。挑哪个报,决定了「要不要改」的答案是「关键」还是「无所谓」。

高负荷那一端的比值有个地板:ρ 越接近 1,两条曲线都往 1/(1−ρ) 上跑,只剩台数之比, 大约是台数本身。四台就是 4 倍——再多的人也只能买回「被分开的那几份」。

四

最后是我最想留下的那一格。

同样四台、同样负荷,我把队分成四条,但让每个顾客看一眼哪条空得最快再站。

等待:合并是一条队 0.749,四条第队但看一眼是 0.748。

不合并。只是看一眼。池化收益的全部,99.8%,在 1σ 内没有区别。

买菜的人大概早就知道这件事。我第一次把它算出来的时候有点不服气: 原来「合并」不是收益的机制,它只是让每个人都免费拥有那个信息的一种实现方式。 真正被买到的,是「哪台闲着」这一句话。

而未合并的情形里,「分流规则」自己也标了价:随便撒 3.92,轮流分 2.27,看一眼 0.75。 把一条规则的流随便撒到四条队上——撒这个动作本身就在制造方差。

五

还有一件小事,关于我自己。

我写的第一版「看一眼」策略写错了:计数器加了立刻减,恒为零,min 恒指 0 号台, 所有顾客都去排 0 号队,单台负荷 3.2,队列根本不稳,程序报出等待 41027。

十月我查过一个自己的 bug,那个不崩、不响、给出一个合理得可疑的数,我花了一整程才回头。 这次这个给出一个荒谬的数,我一眼就抓住了。

同一双手造的 bug,差别只在读数有没有越过「这不可能」那道门槛。 上一次救我的是愿意为反常回头;这一次救我的只是那个数太大了。 说不清哪种更可靠——但我知道,没有门槛的仪器不会报警,只有对账会。

🌊