在机场,出租车调度一直是个麻烦事。司机们聚集在蓄车池排队,等着拉客。管理人员手里有乘客的目的地信息,但怎么用这些信息来平衡司机的收益和乘客的等待时间?特别是那些拉短途乘客的司机,跑一趟赚得少,回来还得重新排队,很容易产生矛盾。常见的做法是给短途司机一些补偿,或者让他们走一个专门的快速通道,提前接客。但补偿多少合适?通道怎么设计?这就涉及排队论和博弈论了。
这个问题的核心是设计一套调度方案,让乘客少等、司机别亏、机场运转高效。听起来像是一个多目标优化,但实际建模时我们得先把它拆开。
排队模型:先看系统怎么运转
机场的客流不是均匀的。飞机到达大致有班次,但延误、乘客出站速度都会带来波动,可以近似成泊松过程。出租车也一样,受路况影响,到达时间间隔往往服从某种随机分布。假设乘客平均到达率是 λ_p 人/小时,出租车 λ_d 辆/小时,其中有一部分 ρ 是短途乘客,司机的平均服务时间倒数是 μ。先用最简单的 M/M/1 排队模型估计一下平均等待时间:
W_q = λ / (μ(μ - λ))
但这太理想了。实际需要区分短途和普通乘客,还要引入非抢占式优先级——短途司机享有优先权,但不会打断正在服务的车辆。于是等待时间变成:
W_q_short = (λE[S^2]) / (2(1 - ρ_short))
W_q_long = (λE[S^2]) / (2(1 - ρ_short)(1 - ρ_short - ρ_long))
其中 E[S^2] 是服务时间的二阶矩,ρ_short 和 ρ_long 分别是短途和长途的流量强度。这个公式能看出来,短途优先确实会让长途乘客的等待时间变长,但只要 ρ_short + ρ_long < 1,系统还是稳定的。
司机怎么想?一个博弈论视角
补偿机制得让司机觉得值才行。单看一趟短途:基本运费 + 里程费 × 距离 - 时间成本 - 可能的空返成本。如果跑完短途回来还要排队很久,那还不如走普通队等个大活。所以司机群体会自发形成一个策略比例:有多少人愿意去短途通道排队?
用混合策略均衡可以描述这个状态。假设选择短途通道的司机比例为 q,均衡条件就是两种选择的期望收益相等:
E[收益 | 短途] = E[收益 | 普通]
这个方程依赖于队列长度、短途比例、补偿力度等变量。解出 q,你就知道在给定参数下,司机会怎么选。当然,现实里的司机不是完全理性的,但这个模型至少给出了一个基准。
Python 模拟:把模型跑起来
公式是静态的,模拟才能看到动态效果。下面这个 TaxiSimulator 类用到了 scipy 的指数分布来生成乘客和司机的到达间隔,然后按事件推进:乘客来了生成行程距离(区分长短途),司机来了决定进哪个队列——这里用了一个简化的决策:根据当前乘客队列长度占出租车队列的比例来随机选择短途通道,比例越高,司机越可能去短途队碰运气。匹配时优先照顾短途队列,超级短途还给额外补偿,空返成本假设为 0.5 元/km。
import numpy as np
from scipy.stats import expon, uniform
class TaxiSimulator:
def __init__(self, passenger_rate=50, taxi_rate=60, short_ratio=0.3,
d_threshold=, base_fare=, fare_per_km=,
waiting_cost=, empty_return_ratio=):
.passenger_rate = passenger_rate
.taxi_rate = taxi_rate
.short_ratio = short_ratio
.d_threshold = d_threshold
.base_fare = base_fare
.fare_per_km = fare_per_km
.waiting_cost = waiting_cost
.empty_return_ratio = empty_return_ratio
.passenger_queue = []
.taxi_queue = []
.short_taxi_queue = []
.time =
.stats = {
: [],
: [],
: ,
:
}
():
is_short = np.random.random() < .short_ratio
is_short:
np.random.uniform(, .d_threshold)
:
np.random.exponential(scale=) + .d_threshold
():
passenger_arrivals = expon.rvs(scale=/.passenger_rate, size=(.passenger_rate * hours * ))
taxi_arrivals = expon.rvs(scale=/.taxi_rate, size=(.taxi_rate * hours * ))
passenger_times = np.cumsum(passenger_arrivals)
taxi_times = np.cumsum(taxi_arrivals)
p_idx = t_idx =
p_idx < (passenger_times) t_idx < (taxi_times):
passenger_times[p_idx] <= taxi_times[t_idx]:
dist = .generate_distance()
.passenger_queue.append((passenger_times[p_idx], dist))
p_idx +=
:
current_short_ratio = (.passenger_queue) / ((.taxi_queue) + ) .passenger_queue
np.random.random() < current_short_ratio * :
.short_taxi_queue.append(taxi_times[t_idx])
:
.taxi_queue.append(taxi_times[t_idx])
t_idx +=
.match_passenger_taxi()
.stats
():
.passenger_queue (.taxi_queue .short_taxi_queue):
arrival_time, dist = .passenger_queue[]
is_short = dist <= .d_threshold
is_short .short_taxi_queue:
taxi_time = .short_taxi_queue.pop()
queue_type =
.taxi_queue:
taxi_time = .taxi_queue.pop()
queue_type =
:
wait_time = (.time - arrival_time, )
.stats[].append(wait_time)
fare = .base_fare + .fare_per_km * dist
is_short dist < .d_threshold / :
fare +=
np.random.random() < .empty_return_ratio:
profit = fare - dist *
:
profit = fare
.stats[].append(profit)
is_short:
.stats[] +=
:
.stats[] +=
.passenger_queue.pop()
sim = TaxiSimulator(passenger_rate=, taxi_rate=, short_ratio=)
results = sim.run_simulation(hours=)
()
()
()
()

