第四节:随机交通流生成——泊松过程与随机种子的秘密
做交通仿真这么多年,我踩过最大的坑是什么?
说出来你可能不信——是「随机」。有一次我跑了一整夜的仿真,早上兴冲冲去看结果,发现跟昨天跑出来的数据差了30%。我当时就懵了,明明参数一模一样啊!后来才反应过来,是随机种子没固定。
嗯,这节课我们就来聊聊这个事。
4.1 为什么用泊松过程?
先问个问题:真实道路上的车流,到底是怎么来的?
你站在路边观察,会发现车辆到达的时间间隔是不固定的。有时候连续来三辆车,有时候等一分钟都没车。这种「随机到达」的特性,用数学语言描述就是——泊松过程。
泊松过程的核心假设其实很简单:
- 任意两个不相交的时间段内,车辆到达数量相互独立
- 在足够小的时间段内,最多只有一辆车到达
- 车辆到达的平均速率是恒定的(比如每小时600辆)
说白了,就是「车来的概率只跟时间长度有关,跟具体几点没关系」。我当年在做一个城市主干道仿真时,实测数据跟泊松模型拟合得特别好,误差不到5%。当然,如果是信号灯路口附近,这个假设就要打折扣了——红灯一拦,车都堵成一团,哪来的随机性?
核心公式:
在时间 t 内,有 k 辆车到达的概率:
P(k) = (λt)^k * e^(-λt) / k!
其中 λ 是平均到达率(辆/秒),t 是时间长度(秒)。
4.2 代码实现:生成泊松车流
我个人习惯用「逆变换法」来生成泊松到达时间。思路很简单:
- 生成一个 [0,1] 均匀分布的随机数 u
- 计算车头时距:Δt = -ln(1-u) / λ
- 累加时间戳,就得到了每辆车的到达时刻
看代码:
import numpy as np
def generate_poisson_traffic(lambda_rate, duration, seed=42):
"""
生成泊松交通流
:param lambda_rate: 平均到达率(辆/秒)
:param duration: 仿真时长(秒)
:param seed: 随机种子
:return: 车辆到达时间列表
"""
np.random.seed(seed)
arrival_times = []
current_time = 0.0
while current_time < duration:
# 生成车头时距
u = np.random.random()
headway = -np.log(1 - u) / lambda_rate
current_time += headway
if current_time < duration:
arrival_times.append(current_time)
return arrival_times
# 示例:平均每小时600辆车,仿真1小时
lambda_rate = 600 / 3600 # 0.167 辆/秒
cars = generate_poisson_traffic(lambda_rate, 3600, seed=123)
print(f"生成了 {len(cars)} 辆车")
print(f"前5辆车到达时刻:{cars[:5]}")
运行一下,你会发现每次生成的车辆数都在600辆左右浮动。这就是泊松过程的特性——均值稳定,但每次具体数值不一样。
4.3 随机种子:那个让我熬夜的罪魁祸首
回到开头那个故事。为什么同样的参数,两次仿真结果差那么多?
因为我没有设置随机种子。计算机生成的随机数其实是「伪随机」的——它有一个初始值,叫种子。种子固定了,随机数序列就固定了。种子变了,整个车流就变了。
重要提醒:
做仿真实验时,一定要记录随机种子!否则你无法复现结果,也无法判断性能差异到底是算法改进带来的,还是随机波动造成的。
来看看不同种子对结果的影响:
| 随机种子 | 生成车辆数 | 平均车头时距(秒) | 最大车头时距(秒) |
|---|---|---|---|
| 42 | 612 | 5.88 | 28.3 |
| 123 | 587 | 6.13 | 31.7 |
| 999 | 605 | 5.95 | 26.9 |
| 2024 | 593 | 6.07 | 33.2 |
看到了吗?同样是600辆/小时的流量,不同种子下车辆数在587到612之间波动。最大车头时距甚至能差到6秒多。这在仿真里意味着什么?
意味着你可能因为随机种子选得好,得出「这个信号配时方案很棒」的结论;换个种子,结论就变成「这个方案不行」。我曾经就吃过这个亏,被评审专家一句话怼回来:「你跑了多少次仿真?统计显著性检验做了吗?」
我的建议:
做对比实验时,至少跑10个不同的随机种子,取平均值和标准差。如果两个方案的平均值差异小于标准差,那基本可以认为它们没有显著区别。
4.4 泊松过程的知识体系
下面这张图,是我梳理的泊松交通流生成的核心逻辑。你一看就明白了:
4.5 避坑指南:我踩过的那些坑
最后分享几个实战经验,都是真金白银换来的:
- 不要用默认种子——很多编程语言的随机数生成器默认种子是系统时间。这意味着你每次运行结果都不一样。我曾经在给客户演示时,第一次跑效果完美,第二次跑直接崩了,场面一度非常尴尬。
- 多线程仿真要小心——如果你用多线程并行跑仿真,每个线程的随机种子要独立设置。否则两个线程可能生成一模一样的车流,那你的「并行」就白费了。
- 记录种子到日志——我现在的习惯是,每次仿真开始前,把随机种子、参数、版本号全部写入日志文件。这样出了问题,随时可以回放。
- 泊松过程不是万能的——在高速公路自由流段,泊松模型很好用。但在城市道路、信号灯影响区域、或者拥堵状态下,车辆到达就不再是随机的了。这时候需要用更复杂的模型,比如马尔可夫调制泊松过程(MMPP)。
嗯,关于随机交通流生成,核心就是这些。记住一句话:随机不是随意,种子决定一切。下次做仿真,别忘了先设个种子。
本节要点回顾:
- 泊松过程适用于描述随机、独立的车辆到达
- 逆变换法生成车头时距:Δt = -ln(1-u)/λ
- 随机种子固定才能复现结果
- 不同种子下结果有波动,需要多次运行取统计值
可可精品资料库