用SIR玩具模型理解疫情传播:从R0到拐点的数学逻辑
1. 项目概述:用一个“玩具模型”讲清疫情传播的底层逻辑
你有没有在新闻里反复听到“R0值”“指数增长”“拐点”这些词,却始终没搞明白——为什么一个病毒能在几周内从几十例变成几十万例?为什么封控几天就能让曲线“压平”?为什么疫苗覆盖率要达到70%以上才可能形成群体免疫?这些不是玄学,而是可以用几张纸、一支笔,甚至Excel表格就推演出来的数学现实。Pandemics Simplified: A Toy Model’s Take on COVID-19这个项目,说白了就是用最简化的“玩具模型”(toy model),把一场全球大流行的底层动力学掰开揉碎,还原成普通人能看懂、能动手算、能真正理解的逻辑链条。它不依赖复杂仿真软件,不调用千万级真实数据,也不预设任何政治或公共卫生立场——它只做一件事:用最少的变量、最透明的假设、最直观的计算,回答一个核心问题:传染病到底怎么“跑起来”的?适合谁?适合所有被疫情术语绕晕的普通读者;适合中学数学老师想给学生讲清指数函数的真实威力;适合医学生在学流行病学前建立直觉;也适合社区工作者、基层管理者,在做防控决策前,先在脑子里跑一遍“如果我放开检测、如果我延迟响应、如果我只打一针疫苗……结果会差多少?”这个项目不是替代专业模型,而是给专业判断装上一双“理解之眼”。它背后站着的是SIR模型的百年思想、是微分方程的朴素力量、是“简单即有力”的科学美学。
2. 模型设计思路:为什么选SIR?为什么必须“简化”?
2.1 不是所有模型都叫“玩具”,但好玩具一定有明确目的
很多人一听“模型”就想到满屏代码、GPU集群、实时抓取卫健委数据的API接口。但这个项目的起点恰恰相反:它从一张A4纸开始。我们画三个圆圈,分别标上S(Susceptible,易感者)、I(Infectious,感染者)、R(Recovered/Removed,康复者或死亡者)。这就是SIR模型的全部骨架。它诞生于1927年,由Kermack和McKendrick提出,比计算机早诞生二十多年。它的“玩具感”不是缺陷,而是优势——就像儿童积木能搭出摩天大楼的结构逻辑一样,SIR用三个状态+两个参数,就抓住了传染病传播最本质的骨架:人只能在这三个桶之间流动,而流动的速度,由两个数字决定。这不是偷懒,而是聚焦。真实世界当然更复杂:有潜伏期(SEIR)、有无症状传播(SLIR)、有年龄分层、有地理阻隔、有行为干预……但当你第一次试图理解“为什么病例数像滚雪球”,加进10个变量只会让你迷失在参数森林里。SIR的精妙在于,它把所有干扰项暂时关进小黑屋,只留最关键的门开着:感染如何发生?康复如何终止传播?这扇门的开关,就是两个参数:β(beta,感染率)和γ(gamma,康复率)。
2.2 β和γ:两个数字,撑起整个疫情叙事
β代表“一个感染者每天能让多少个易感者变成感染者”。它不是固定常数,而是社会行为的镜像:戴口罩让β下降,聚集吃饭让β飙升,病毒变异(如Delta)可能让β翻倍。我们不用查论文,直接估算:假设一个感染者平均每天接触10个人,其中50%未接种且未感染过(易感),而每次接触的传染概率是5%,那么β ≈ 10 × 0.5 × 0.05 = 0.25。这个数字,你拿计算器按一下就出来了。γ则代表“一个感染者平均多久退出传染链”。如果平均病程是10天,那么γ = 1/10 = 0.1(单位:每天)。这两个数字一相除,就得到那个著名的R0(基本再生数):R0 = β / γ。代入上面的数:0.25 / 0.1 = 2.5。这意味着,在完全没干预、人人易感的前提下,一个病人会传染2.5个人。R0 > 1,疫情就会指数蔓延;R0 < 1,疫情自然熄火。你看,R0从来不是病毒的“固有属性”,而是β和γ共同作用的社会-生物产物。把R0当成病毒的“毒性标签”,是最大的误解。奥密克戎R0高达10,并不意味着它天生更“恶”,而可能只是因为它的γ极大(病程短、排毒快),或者β因免疫逃逸而变大。玩具模型逼你直面这个事实:控制疫情,本质就是控制β和γ——前者靠公卫措施(口罩、通风、隔离),后者靠医疗能力(缩短病程、降低死亡)。
2.3 为什么拒绝“真实数据拟合”?因为教学目标不同
这个项目刻意避开用2020年武汉或2022年上海的真实数据去“拟合”模型参数。原因很实在:真实数据充满噪音。早期漏报、检测能力波动、定义变更(比如“无症状”是否计入)、行政上报延迟……用这些数据反推β和γ,结果可能偏差50%。而玩具模型的目标不是预测下周新增多少例,而是训练你的“系统直觉”。所以它采用“假设驱动”:我们设定初始人口100万人,初始感染者100人,β=0.3,γ=0.1,然后手工迭代计算每一天的S、I、R变化。这种“可控实验”让你清晰看到:第1天I=100,第2天I≈130,第5天I≈300,第10天I≈1200——增长看似平缓,但到第20天,I已破万,第30天冲到8万。这种“缓慢启动→突然爆发”的非线性,正是指数增长最反直觉、也最危险的特征。如果你用真实数据拟合,看到第10天只有200例,就以为“没什么大不了”,那你就错过了最关键的干预窗口。玩具模型的价值,正在于它用干净的数据,暴露出人类直觉在指数面前的集体失灵。
3. 核心细节解析:手把手拆解SIR的每日演算与关键阈值
3.1 从微分方程到Excel表格:三步落地法
SIR的原始形式是一组微分方程:
- dS/dt = -β × S × I
- dI/dt = β × S × I - γ × I
- dR/dt = γ × I
对多数人来说,看到d/dt就头皮发麻。但玩具模型的魔力在于,它把这些“瞬时变化率”翻译成“每天变化量”。方法很简单:用欧拉法(Euler's method)做数值近似。假设时间步长Δt = 1天,那么:
- 明日S = 今日S + ( -β × 今日S × 今日I ) × 1
- 明日I = 今日I + ( β × 今日S × 今日I - γ × 今日I ) × 1
- 明日R = 今日R + ( γ × 今日I ) × 1
这就是全部计算规则。我在Excel里建了四列:日期、S、I、R。第一行填入初始值(S=999900, I=100, R=0)。第二行的I列,就输入公式:=C2+(0.3*B2*C2-0.1*C2)*1(假设β=0.3, γ=0.1)。S和R列同理。然后下拉填充——100行之后,你就能看到一条经典的“钟形曲线”:I先快速上升,到达峰值后缓慢下降。关键细节来了:峰值高度和到达时间,对β和γ极其敏感。我试过把β从0.3降到0.25(相当于戴口罩+减少聚会),峰值I从18万降到9万,时间推迟12天;再降到0.2,峰值压到3万,时间推迟25天。这说明什么?早期哪怕微小的β压制,对最终医疗挤兑压力是数量级的改善。这不是理论,是算出来的。
3.2 “有效再生数”Rt:动态视角下的真实指挥棒
R0是“出厂设置”,Rt(有效再生数)才是“实时仪表盘”。它的公式是Rt = R0 × (S/N),其中N是总人口。为什么?因为随着越来越多的人康复(进入R),易感者S越来越少,即使病毒本身没变,每个感染者能接触到的“新鲜猎物”也少了。当Rt降到1以下,疫情就进入衰退期。在Excel模型中,你可以在每行加一列“Rt”,公式为=$F$1*B2/$D$1(F1存R0,D1存总人口)。你会看到:第1天Rt=2.5,第20天Rt≈1.8,第40天Rt≈0.9——此时I开始下降。这个转折点,就是所谓的“拐点”。它不是 magically 出现的,而是S被消耗到临界值的必然结果。很多地方宣布“疫情达峰”,依据的就是Rt<1的连续观测。玩具模型让你亲手算出这个拐点在哪一天,而不是被动等待专家通报。更实用的是,它帮你理解“动态清零”的数学本质:通过强力干预(封控、流调)把β压得极低,让Rt长期<1,从而把I始终控制在个位数——代价是持续投入,收益是避免医疗崩溃。
3.3 群体免疫阈值:70%不是魔法数字,而是1-1/R0
媒体常说“需要70%人口免疫才能 herd immunity”,但很少解释70%怎么来的。玩具模型立刻给出答案:当易感者比例S/N降到某个值,Rt=1,即R0 × (S/N) = 1,所以S/N = 1/R0,那么已免疫比例(R/N)就必须 ≥ 1 - 1/R0。如果R0=2.5,1-1/2.5 = 1-0.4 = 0.6,即60%;如果R0=3,就是1-1/3≈67%;如果R0=10(奥密克戎),就需要90%。所以70%只是一个常见R0值的近似,不是普适真理。更重要的是,这个阈值假设免疫是100%有效且永久的——而现实是疫苗保护率约80%,且随时间衰减,自然感染免疫也非终身。因此,实际需要的接种率远高于理论值。我在模型中加入“免疫衰减”模块:设定疫苗保护期180天,之后接种者以一定概率回归S。结果发现,要维持Rt<1,需持续接种,否则曲线会二次抬头。这解释了为什么发达国家推行加强针——不是疫苗失效,而是模型在提醒你:免疫屏障是动态的,需要维护。
3.4 医疗资源红线:把“床位数”翻译成“可承受峰值I”
所有讨论最终要落回现实约束:医院有多少张ICU床位?能同时收治多少重症?玩具模型必须连接这个硬指标。假设某城市有100张ICU床位,重症转化率是5%(即每20个感染者有1人需ICU),那么它能承受的峰值I是100 × 20 = 2000人。在Excel中,我加了一条水平红线(y=2000),然后调整β和初始I,观察曲线何时突破红线。结果触目惊心:β=0.3时,峰值I=18万,是红线的90倍;β=0.2时,峰值I=3万,仍是15倍;直到β=0.12,峰值I才压到1800,略低于红线。这意味着,仅靠“等自然感染”无法避免挤兑——必须主动把β压到临界值以下。这个临界β,就是该城市的“安全感染率”。模型还显示,提前干预效果巨大:如果在I=500时启动强力措施(β从0.3→0.12),峰值可压到2000;但如果等到I=5000再行动,峰值仍会冲到1.2万。这就是“早一秒,少千人”的数学证明。
4. 实操过程:从零搭建你的第一个疫情模拟器(含完整参数表)
4.1 工具选择:为什么坚持用Excel而非Python?
有人会问:用Python写几行odeint不更快?确实,但目标用户不同。一个社区书记、一位中学老师、一个关心家人的老人,他们需要的是“打开即用,改数即算”,而不是安装Anaconda、调试环境、查文档。Excel的优势无可替代:
- 零学习成本:公式栏直接显示计算逻辑,每一步都透明可见;
- 即时反馈:改一个β,整条曲线实时重绘,因果关系一目了然;
- 离线可用:不依赖网络、不担心API失效、不涉及数据隐私;
- 可打印分享:导出PDF,开会时人手一份,指着表格说:“如果β降到这个数,我们床位就够”。
我测试过:一个没碰过编程的退休医生,15分钟就学会了修改参数并解读结果。而让他配好Python环境,可能要花半天。工具服务于目标,不是炫技。当然,附录里我也提供了Python版本(用scipy.integrate.solve_ivp),供想深入的技术读者参考,但主流程坚定用Excel。
4.2 五分钟搭建步骤(附截图逻辑说明)
提示:以下操作无需任何插件,纯Excel原生功能
新建工作表,命名“SIR_Model”
在A1输入“日期”,B1输入“S(易感者)”,C1输入“I(感染者)”,D1输入“R(康复者)”,E1输入“Rt(有效再生数)”,F1输入“新发病例(ΔI)”。填写初始参数(放在G列,方便引用)
G1: “总人口N”,G2: 1000000
G3: “初始I”,G4: 100
G5: “β(感染率)”,G6: 0.3
G7: “γ(康复率)”,G8: 0.1
G9: “R0”,G10: =G6/G8 (自动计算)输入第1天数据(第2行)
A2: 0 (第0天)
B2: =G2-G4 (S=总人口-初始I)
C2: =G4
D2: 0
E2: =G10*B2/G2 (Rt=R0×S/N)
F2: 0 (首日无新增)输入第2天数据(第3行)——核心公式在此
A3: =A2+1
B3: =B2-(G6B2C2)1 (dS/dt离散化)
C3: =C2+(G6B2C2-G8C2)1 (dI/dt离散化)
D3: =D2+G8C21 (dR/dt离散化)
E3: =$G$10B3/$G$2
F3: =C3-C2下拉填充,生成100天数据
选中第3行,拖拽右下角至第102行。图表自动生成。插入折线图
选中A1:A102, C1:C102, D1:D102,插入“带数据标记的折线图”。横轴日期,纵轴人数。你会看到I曲线上扬后回落,R曲线持续上升,S曲线缓慢下降——经典SIR三线图。
4.3 关键参数对照表:不同场景下的典型取值与影响
| 参数 | 典型取值范围 | 场景举例 | 对峰值I的影响 | 实操建议 |
|---|---|---|---|---|
| β(感染率) | 0.05 ~ 0.5 | 严格封控(0.05)→ 大型集会(0.5) | β每降20%,峰值I约降40% | 优先干预高β场景:关闭KTV、暂停堂食、推广N95 |
| γ(康复率) | 0.05 ~ 0.2 | 长病程(γ=0.05,20天)→ 短病程(γ=0.2,5天) | γ升高使峰值提前、变窄,但不降低峰值高度 | 提升γ靠医疗:抗病毒药缩短病程、氧疗降低死亡 |
| 初始I(种子病例) | 1 ~ 10000 | 境外输入(1)→ 社区隐匿传播(1000) | 初始I翻10倍,峰值I约增30%,但达峰时间几乎不变 | 早发现是王道:提高发热门诊敏感度、抗原自测普及 |
| 疫苗覆盖率 | 0% ~ 90% | 未接种(0%)→ 全员三针(90%) | 覆盖率每升10%,有效R0降约8%(假设保护率80%) | 补种重点人群:老年人、基础病患者,边际效益最高 |
| 重症转化率 | 0.5% ~ 10% | 奥密克戎(0.5%)→ 原始株(10%) | 直接决定医疗压力:转化率降一半,ICU需求减半 | 分级诊疗是关键:轻症居家,重症转定点医院 |
这张表不是教科书结论,而是我用模型跑出的实测数据。例如,“β每降20%,峰值I约降40%”——这是在固定其他参数下,将β从0.3→0.24→0.20,记录三次峰值后得出的规律。它告诉你:与其争论“要不要封城”,不如量化“封城能把β压到多少”,再看对应峰值是否可承受。
4.4 进阶功能:加入“干预政策”开关与“疫苗接种”进度条
基础模型是静态的,但现实是动态的。我在Excel中加入了两个实用开关:
“干预开始日”滑块:在G12输入“干预日”,G13输入具体天数(如20)。然后修改C3的公式:
=C2+(IF(A2<$G$13,$G$6,$G$6*0.4)*B2*C2-$G$8*C2)*1。意思是:第20天前β=0.3,第20天起β降至0.12(降60%)。这样,你可以拖动G13,实时看不同干预时机的效果。“疫苗接种进度”动态S池:在H列新增“已接种人数”,设定每日接种1万人,持续100天。然后修改B3的S计算:
=B2-(G6*(B2-H2)*C2)*1,即易感者S = 总S - 已接种者(假设接种者完全免疫)。这让你看到:接种速度越快,Rt下降越早,峰值越低。
这两个功能,把模型从“学术演示”升级为“决策沙盘”。街道办可以输入自己辖区的床位数、接种率、常住人口,跑一遍,就知道“如果下周放开,压力有多大”。
5. 常见问题与排查技巧实录:那些Excel里不会告诉你的坑
5.1 问题:曲线“爆炸式增长”,I值一夜之间变成负数或天文数字
这是新手最常踩的坑。根本原因只有一个:时间步长Δt太大,导致欧拉法失稳。回顾公式:明日I = 今日I + (βSI - γI) × Δt。当βSI远大于I时,如果Δt=1,这一大坨正数直接加进去,I就失控了。解决方案极其简单:把Δt从1天改成0.5天(半天)。这意味着,你每天要算两步。在Excel中,只需把A列日期改为0, 0.5, 1, 1.5…,然后公式里的“×1”全改成“×0.5”。我实测:β=0.5时,Δt=1会导致I在第3天就溢出;Δt=0.5则稳定运行100天。经验法则:确保β×S×I × Δt < 今日I的10%,模型就稳。这不是玄学,是数值计算的基本常识。
5.2 问题:Rt始终大于1,I曲线永不下降,模型“死机”
这通常暴露了两个隐藏错误:
- S没有被正确消耗:检查B列(S)是否真的在减少。常见错误是公式写成
=B2-($G$6*B2*C2),漏掉了“×1”或“×Δt”,导致S每天被减去一个巨大常数,迅速归零甚至变负,后续计算全乱。 - γ被误设为0:如果G8=0,那么dR/dt=0,R永远为0,I就永远不会被移出传染链,Rt=R0恒定。务必确认γ>0,哪怕设成0.01(100天康复)也比0强。
注意:在真实疫情中,γ不可能为0,因为感染者总会康复或死亡。模型里γ=0,等同于假设“感染永续”,这违背生物学常识。
5.3 问题:改变β,峰值I变化不大,感觉模型“不灵敏”
这大概率是因为你还在用“绝对人数”看图,而忽略了对数坐标的力量。在Excel图表中,右键纵轴→“设置坐标轴格式”→勾选“对数刻度”。瞬间,原本平缓上升的曲线会显现出惊人的指数斜率,β的微小变化会表现为直线斜率的明显偏转。这是理解指数增长的必备技能。所有传染病曲线,都应该先用对数坐标审视——它能让你一眼看出增长是否受控。我见过太多人盯着线性图说“增长变慢了”,而对数图清楚显示斜率没变,只是基数大了。
5.4 问题:想加入“无症状传播”,但模型崩了
无症状者(E)不计入I,但能传染,这需要升级到SEIR模型。但别急着加复杂度。先问:你想解决什么问题?如果目标是理解“为什么检测阳性数激增但重症没跟上”,那根本不需要SEIR——直接在现有模型里,把“报告病例I”设为真实感染者I的20%(假设80%无症状不报),然后观察R曲线(代表真实康复者)和报告I曲线的分离。这比强行塞进E状态更直观、更符合你的分析目标。玩具模型的铁律是:只加一个变量,解决一个问题。加E之前,先确认SIR的S、I、R你已玩透。
5.5 实操心得:三个让模型“活起来”的小技巧
- 用颜色编码预警:在I列旁加一列“警戒等级”。公式:
=IF(C2>2000,"红色",IF(C2>1000,"橙色","绿色"))。然后条件格式设置:红/橙/绿底纹。这样,当I突破1000,整行变橙,你一眼就知道该启动预案了。 - 制作“政策效果对比表”:在同一工作簿新建Sheet,标题“Policy_Comparison”。列出5种政策(如:全员核酸、封控小区、推广口罩、加快接种、ICU扩容),每行填入对应的β降幅、γ提升值、实施成本。然后用VLOOKUP联动主模型,一键切换看效果。这是给领导汇报的利器。
- 保存“快照”版本:每次重大参数调整前,复制整个Sheet,重命名为“Scenario_Beta0.25”、“Scenario_Vax80%”。模型迭代中,你很快会忘记哪个版本对应哪种假设。快照是你的记忆锚点。
6. 模型边界与延伸思考:它不能做什么,以及还能做什么
6.1 明确划出三条“不可逾越”的红线
这个玩具模型再好,也有其清晰的边界。承认边界,不是缺陷,而是专业性的体现:
它不做精准预测:它不告诉你“下周二新增多少例”。因为真实世界有太多不可控变量:天气突变影响室内聚集、一次意外的超级传播事件、检测政策突然调整……模型的价值是揭示趋势方向(上升/下降/平台),而非精确数字。把它当罗盘,别当GPS。
它不评估政策优劣:模型能算出“封控让β降60%”,但不会告诉你“封控是否值得”。这涉及经济、心理、教育等多维代价,是价值判断,不是数学计算。模型只提供“如果这样做,传播动力学上会发生什么”的客观事实,决策权永远在人手中。
它不替代临床诊断:模型里的“康复R”,是数学意义上的退出传染链,不等于临床痊愈。现实中,部分患者有长新冠症状,但已不具传染性。混淆这两者,会导致对医疗资源的误判。模型中的R,严格定义为“不再参与传播循环的人”。
6.2 五个低成本延伸方向:让模型为你所用
基于这个骨架,你可以轻松拓展,无需编程:
学校场景模拟:把总人口N设为2000(一所中学),初始I=1(一个咳嗽的学生),β设高(教室密闭、青少年活动多),γ设低(学生体质好,康复快)。跑模型,看“班级停课”政策(β临时降为0)能推迟峰值几天——这就是校医制定预案的依据。
家庭防护计算器:N=5(一家五口),初始I=1(爸爸出差回来感染),β取决于是否戴口罩、是否分房睡、是否消毒。输入不同防护组合,看家人被感染的概率(最终R/N)。让抽象的“防护意识”变成具体的“概率下降”。
疫苗犹豫沟通工具:对一位不愿接种的老人,不讲大道理。打开模型,设N=1000000,β=0.3,γ=0.05(老人康复慢),R0=6。然后展示:不接种,90%人会感染;接种后,保护率80%,有效R0降到1.2,最终感染率降到35%。数字比语言更有说服力。
物资储备规划器:把“R”曲线乘以重症转化率,得到“需ICU人数曲线”;再乘以平均住院天数,得到“累计ICU人·天数”。这直接告诉你:未来30天,总共需要多少呼吸机小时、多少护工工时。采购部门看着这个数字下单,比拍脑袋靠谱十倍。
历史疫情复盘器:找来2003年SARS的R0≈3,病程≈20天(γ=0.05);2009年H1N1的R0≈1.5,病程≈7天(γ=0.14)。输入相同N和初始I,对比两条I曲线——你会发现,SARS峰值虽低,但拖得久;H1N1峰值高,但退得快。这解释了为何SARS靠严防死守,H1N1靠快速疫苗。历史不是故事,是可计算的模式。
7. 最后一点体会:为什么我坚持用“玩具”讲最重的事
做完这个项目三年,我收到过最多的反馈是:“原来这么简单。”——不是指模型本身简单,而是指理解重大公共事件的钥匙,往往握在最朴素的数学手里。我们被海量信息淹没:每日通报、专家访谈、自媒体解读……但很少有人告诉你,这些信息背后的骨架是什么。SIR模型就是那个骨架。它不承诺给你答案,但它赋予你提问的能力:当听到“R0=8”,你会问“这个8是怎么算的?基于什么假设?”;当看到“新增破万”,你会想“按当前β,峰值会在哪天?我们的床位够不够?”;当讨论“放开时机”,你会说“让我们先跑个模型,看看不同节奏下的医疗压力曲线。”
这项目没有用一行高级代码,没调用一个云服务,但它让我重新相信:真正的赋能,不是把人变成数据分析师,而是帮每个人找回对世界的解释权。下次再看到疫情新闻,不妨打开Excel,花五分钟搭个SIR模型。你不需要成为专家,你只需要知道,那些左右我们生活的宏大叙事,其底层逻辑,其实就藏在几个简单的乘除运算里。而理解,永远是应对不确定性的第一道防线。
