2026美赛期间会持续更新相关内容,所有内容会发布到专栏内,会结合最新的chatgpt发布,只需订阅一次,赛后两天半价,内容达不到所有人预期,请勿盲目订阅!!!无论文!无论文!!!
摘要
常微分方程(Ordinary Differential Equations, ODEs)作为描述系统动态演化的数学工具,在数学建模中占据核心地位。本文从数学建模竞赛的实用视角出发,系统阐述ODE在人口增长和传染病SIR模型中的应用。首先,深入探讨ODE的核心思想与数学模型,包括基本概念、分类和解的结构特性。其次,分析ODE在建模竞赛中的适用场景与典型赛题类型。接着,详细说明具体建模步骤与关键技巧,并提供常用求解工具和代码示例。然后,通过一个完整的美赛简化案例展示ODE建模的全过程。最后,评估该方法的优缺点并提出改进方向。本文旨在为数学建模竞赛参与者提供ODE建模的系统性指导,提升解决实际动态系统问题的能力。
关键词:常微分方程;数学建模;人口增长;SIR模型;传染病动力学;数值求解
1. 核心思想与数学模型
1.1 常微分方程基本概念
常微分方程是描述未知函数与其导数之间关系的方程,其中未知函数仅依赖于一个自变量。在数学建模中,时间t常作为自变量,而系统状态变量(如人口数量、感染人数等)作为因变量。ODE的一般形式为:
F(t,y,y′,y′′,…,y(n))=0F(t,y,y′,y′′,…,y(n))=0
其中y=y(t)y=y(t)是未知函数,y′,y′′,…,y(n)y′,y′′,…,y(n)是其关于t的各阶导数。
对于n阶ODE,若存在形式:
y(n)=f(t,y,y′,…,y(n−1))y(n)=f(t,y,y′,…,y(n−1))
则称为显式ODE。初值问题的提法为:在给定初始条件y(t0)=y0,y′(t0)=y0′,…,y(n−1)(t0)=y0(n−1)y(t0)=y0,y′(t0)=y0′,…,y(n−1)(t0)=y0(n−1)下,求解满足ODE的函数y(t)y(t)。
1.2 人口增长模型
1.2.1 Malthus模型(指数增长模型)
马尔萨斯于1798年提出的人口增长模型基于一个基本假设:人口增长率与当前人口数量成正比。该模型表示为:
dPdt=rPdtdP=rP
其中:
-
P(t)P(t):t时刻的人口数量
-
rr:内禀增长率(出生率与死亡率之差)
-
tt:时间
给定初始条件P(0)=P0P(0)=P0,可得解析解:
P(t)=P0ertP(t)=P0ert
该模型预测人口将呈指数增长,适用于资源充足、无限制环境下的短期人口预测。但长期来看,资源限制会使增长放缓,与实际观测不符。
1.2.2 Logistic模型(阻滞增长模型)
1838年,Verhulst提出Logistic模型以修正Malthus模型的缺陷,引入环境承载力K的概念:
dPdt=rP(1−PK)dtdP=rP(1−KP)
其中:
-
KK:环境承载力(最大可持续人口数量)
-
因子(1−P/K)(1−P/K):环境阻力项,随P接近K而减小
该方程可分离变量求解:
dPP(1−P/K)=r dtP(1−P/K)dP=rdt
积分得:
P(t)=K1+(K−P0P0)e−rtP(t)=1+(P0K−P0)e−rtK
Logistic曲线呈S形:初始近似指数增长,随后增长放缓,最终趋于稳定值K。该模型更符合实际观测,广泛应用于生态学、经济学等领域。
1.2.3 改进的人口增长模型
实际应用中,可根据具体情况对Logistic模型进行改进:
时变承载力模型:K=K(t)K=K(t),反映环境变化
dPdt=rP(1−PK(t))dtdP=rP(1−K(t)P)
时变增长率模型:r=r(t)r=r(t),反映政策、技术等因素
dPdt=r(t)P(1−PK)dtdP=r(t)P(1−KP)
带Allee效应的模型:种群密度过低时增长率为负
dPdt=rP(PA−1)(1−PK),0<A<KdtdP=rP(AP−1)(1−KP),0<A<K
考虑年龄结构的模型:将人口按年龄分组,建立方程组
∂P(a,t)∂t+∂P(a,t)∂a=−μ(a,t)P(a,t)∂t∂P(a,t)+∂a∂P(a,t)=−μ(a,t)P(a,t)
其中P(a,t)P(a,t)表示t时刻年龄为a的人口密度。
1.3 传染病SIR模型
1.3.1 经典SIR模型
Kermack和McKendrick于1927年提出的SIR模型是传染病动力学的基础。模型将人群分为三类:
-
易感者(Susceptible, S):可能被感染的健康个体
-
感染者(Infected, I):已感染且能传播疾病的个体
-
移出者(Removed, R):康复并获得免疫力或死亡的个体
模型假设如下:
总人口数N=S+I+RN=S+I+R恒定(不考虑出生、死亡和迁移)
均匀混合:个体随机接触
感染率与SI乘积成正比
恢复率与I成正比
模型方程:
dSdt=−βSINdIdt=βSIN−γIdRdt=γIdtdSdtdIdtdR=−βNSI=βNSI−γI=γI
其中:
-
ββ:感染率(有效接触率)
-
γγ:恢复率(恢复率的倒数为平均感染期1/γ1/γ)
1.3.2 基本再生数R0R0
基本再生数R0R0是传染病动力学的关键参数,表示一个感染者在完全易感人群中引起的平均新感染数。对于SIR模型:
R0=βγR0=γβ
-
若R0>1R0>1,疾病可能流行
-
若R0<1R0<1,疾病逐渐消失
-
R0=1R0=1是疾病传播的阈值
1.3.3 SIR模型的扩展
实际应用中常对SIR模型进行扩展:
SEIR模型:增加潜伏期人群(Exposed, E)
dSdt=−βSINdEdt=βSIN−σEdIdt=σE−γIdRdt=γIdtdSdtdEdtdIdtdR=−βNSI=βNSI−σE=σE−γI=γI
其中σσ为潜伏期到发病的转移率。
SIS模型:康复后无免疫力,重新变为易感者
dSdt=−βSIN+γIdIdt=βSIN−γIdtdSdtdI=−βNSI+γI=βNSI−γI
考虑人口动力学的SIR模型:包含出生和死亡
dSdt=μN−βSIN−μSdIdt=βSIN−γI−μIdRdt=γI−μRdtdSdtdIdtdR=μN−βNSI−μS=βNSI−γI−μI=γI−μR
其中μμ为出生率和死亡率(假设相等)。
空间异质性模型:考虑空间扩散
∂S∂t=DS∇2S−βSI∂t∂S=DS∇2S−βSI∂I∂t=DI∇2I+βSI−γI∂t∂I=DI∇2I+βSI−γI
其中DS,DIDS,DI为扩散系数。
1.3.4 模型分析与相平面
对于SIR模型,注意到dS/dt+dI/dt+dR/dt=0dS/dt+dI/dt+dR/dt=0,故总人口守恒。定义相对比例s=S/N,i=I/N,r=R/Ns=S/N,i=I/N,r=R/N,则:
dsdt=−βsididt=βsi−γidrdt=γidtdsdtdidtdr=−βsi=βsi−γi=γi
相平面分析可在s−is−i平面进行。由前两式得:
dids=−1+γβs=−1+1R0sdsdi=−1+βsγ=−1+R0s1
积分得:
i(s)=(1−s)+1R0ln(ss0)i(s)=(1−s)+R01ln(s0s)
其中s0s0为初始时刻的s值。由此可分析疾病最终规模。
2. 适用场景与典型赛题类型
2.1 ODE建模的适用场景
ODE适用于描述连续时间动态系统,特别是:
种群动力学:人口增长、物种竞争、捕食-被捕食关系
流行病学:传染病传播、信息传播、谣言扩散
生态学:生态系统演化、污染物扩散
物理学:物体运动、电路分析、热传导
经济学:经济增长、市场均衡、投资决策
社会学:观点传播、社会网络演化
药理学:药物代谢、药代动力学
环境科学:污染物浓度变化、碳循环
2.2 数学建模竞赛中的典型赛题类型
2.2.1 人口相关问题
人口预测与规划:给定历史数据,预测未来人口,评估政策效果
老龄化问题:考虑年龄结构,分析养老金、医疗资源需求
资源承载分析:结合资源限制,确定可持续人口规模
移民影响评估:研究移民对人口结构、经济发展的影响
真题示例:2019年美赛C题"毒品问题"涉及药物使用者数量变化,可用类似传染病模型建模。
2.2.2 传染病相关问题
疫情预测:基于早期数据预测疫情发展趋势
干预措施评估:隔离、疫苗接种、社交距离等措施效果分析
医疗资源需求:预测病床、呼吸机等医疗资源需求峰值
疫苗分配策略:有限疫苗资源下的最优分配方案
多群体传播:考虑年龄、职业等不同群体的传播差异
真题示例:2020年美赛D题"团队合作策略"可借鉴传染病模型研究合作行为传播;2021年美赛D题"音乐影响"可用SIR类模型研究音乐偏好传播。
2.2.3 生态与环境问题
物种竞争:Lotka-Volterra竞争模型
dN1dt=r1N1(1−N1+α12N2K1)dN2dt=r2N2(1−N2+α21N1K2)dtdN1dtdN2=r1N1(1−K1N1+α12N2)=r2N2(1−K2N2+α21N1)
捕食-被捕食关系:Lotka-Volterra捕食模型
dHdt=rH−aHPdPdt=ϵaHP−mPdtdHdtdP=rH−aHP=ϵaHP−mP
其中H为猎物,P为捕食者。
污染物扩散:多介质环境中的污染物迁移转化
2.2.4 社会经济问题
技术创新扩散:Bass模型描述新产品、新技术采纳
dN(t)dt=p[M−N(t)]+qMN(t)[M−N(t)]dtdN(t)=p[M−N(t)]+MqN(t)[M−N(t)]
其中p为创新系数,q为模仿系数,M为市场潜力。
谣言传播:类似传染病模型,但考虑遗忘机制
经济周期:用动力学方程描述经济增长波动
2.3 赛题识别与建模选择
面对赛题时,识别是否适用ODE建模的关键指标:
系统状态随时间连续变化
变化率依赖于当前状态
忽略空间异质性或可简化为均值场
随机效应不占主导地位
若系统离散性明显或随机性占主导,应考虑差分方程或随机过程模型。
3. 具体建模步骤与关键技巧
3.1 ODE建模的一般步骤
步骤1:问题分析与假设建立
明确研究问题:确定需要预测、解释或优化的目标
识别系统要素:确定状态变量、参数和控制变量
建立合理假设:简化现实,使问题可处理
-
均匀混合假设
-
忽略随机波动
-
参数恒定性假设
-
封闭系统假设
确定模型类型:根据问题特点选择合适模型框架
步骤2:变量定义与方程建立
定义状态变量:选择恰当变量描述系统状态
建立流程图:可视化各状态间转移关系
推导微分方程:基于质量守恒、速率平衡等原理
-
流入率 – 流出率 = 净变化率
确定参数意义:明确每个参数的物理/生物意义
步骤3:参数估计与数据拟合
参数来源:
-
文献资料
-
实验数据
-
统计年鉴
-
专家经验
估计方法:
-
最小二乘法
-
最大似然估计
-
贝叶斯估计
拟合优度评估:
-
残差分析
-
R²统计量
-
AIC/BIC准则
步骤4:模型求解与分析
解析求解:适用于简单线性方程
数值求解:Runge-Kutta等数值方法
稳定性分析:平衡点分析,Jacobian矩阵
敏感性分析:参数变化对结果的影响程度
分岔分析:参数变化导致系统定性行为改变
步骤5:结果解释与模型检验
结果解释:将数学结果转化为实际问题解答
模型验证:对比模型预测与实际观测
稳健性分析:改变假设条件,检验结果稳定性
模型改进:根据验证结果修正模型
3.2 关键技巧与注意事项
3.2.1 无量纲化技巧
无量纲化可简化方程,减少参数数量,提高数值稳定性。以Logistic方程为例:
原始方程:dPdt=rP(1−PK)dtdP=rP(1−KP)
令x=PKx=KP(相对人口),τ=rtτ=rt(无量纲时间),则:
dxdτ=x(1−x)dτdx=x(1−x)
方程简化为单参数形式,便于分析。
3.2.2 参数敏感性分析
评估参数不确定性对结果的影响。局部敏感性指数:
Sp=∂y∂p⋅pySp=∂p∂y⋅yp
其中y为输出变量,p为参数。全局敏感性分析可采用Sobol指数法。
3.2.3 相平面分析技巧
对于二维自治系统:
dxdt=f(x,y)dydt=g(x,y)dtdxdtdy=f(x,y)=g(x,y)
寻找平衡点:解f(x,y)=0,g(x,y)=0f(x,y)=0,g(x,y)=0
线性化:在平衡点(x∗,y∗)(x∗,y∗)处计算Jacobian矩阵
J=[∂f∂x∂f∂y∂g∂x∂g∂y](x∗,y∗)J=[∂x∂f∂x∂g∂y∂f∂y∂g](x∗,y∗)
稳定性判定:根据特征值实部符号判断稳定性
绘制零倾线:f(x,y)=0f(x,y)=0和g(x,y)=0g(x,y)=0的曲线
3.2.4 数值求解稳定性
步长选择:步长过大会导致不稳定,过小会增加计算量
方法选择:
-
非刚性系统:经典Runge-Kutta法(如RK4)
-
刚性系统:隐式方法(如Gear法、BDF法)
误差控制:自适应步长调整,局部截断误差估计
3.2.5 模型比较与选择
使用信息准则选择最合适模型:
-
AIC(Akaike信息准则):AIC=2k−2ln(L)AIC=2k−2ln(L)
-
BIC(贝叶斯信息准则):BIC=kln(n)−2ln(L)BIC=kln(n)−2ln(L)
其中k为参数个数,n为数据点数,L为似然函数值。
4. 常用求解工具/代码示例
4.1 常用软件工具
MATLAB:ode45, ode23, ode15s等求解器
Python:SciPy的odeint, solve_ivp
R:deSolve包
Mathematica:DSolve, NDSolve
Julia:DifferentialEquations.jl包
4.2 MATLAB代码示例
4.2.1 Logistic人口增长模型
matlab
% Logistic模型参数
r = 0.03; % 年增长率
K = 1000; % 环境承载力(百万)
P0 = 100; % 初始人口(百万)
tspan = [0 100]; % 时间范围(年)
% 定义微分方程
logisticODE = @(t, P) r * P * (1 – P/K);
% 数值求解
[t, P] = ode45(logisticODE, tspan, P0);
% 解析解对比
P_analytic = K ./ (1 + (K-P0)/P0 * exp(-r*t));
% 绘图
figure('Position', [100, 100, 900, 400])
subplot(1,2,1)
plot(t, P, 'b-', 'LineWidth', 2)
hold on
plot(t, P_analytic, 'r–', 'LineWidth', 1.5)
xlabel('时间 (年)', 'FontSize', 12)
ylabel('人口 (百万)', 'FontSize', 12)
title('Logistic人口增长', 'FontSize', 14)
legend('数值解', '解析解', 'Location', 'southeast')
grid on
% 绘制相线
subplot(1,2,2)
P_range = 0:10:1200;
dP_dt = r * P_range .* (1 – P_range/K);
plot(P_range, dP_dt, 'k-', 'LineWidth', 2)
hold on
plot([0 K], [0 0], 'k–')
plot(P0, 0, 'ro', 'MarkerSize', 10, 'MarkerFaceColor', 'r')
xlabel('人口 P', 'FontSize', 12)
ylabel('变化率 dP/dt', 'FontSize', 12)
title('相线图', 'FontSize', 14)
grid on
4.2.2 SIR传染病模型
matlab
function sir_model_example
% SIR模型参数
beta = 0.3; % 感染率
gamma = 0.1; % 恢复率
N = 1000; % 总人口
% 初始条件
I0 = 1; % 初始感染者
R0 = 0; % 初始康复者
S0 = N – I0 – R0; % 初始易感者
% 时间范围
tspan = [0 100];
y0 = [S0; I0; R0];
% 定义SIR方程
function dydt = sir_equations(t, y)
S = y(1);
I = y(2);
R = y(3);
dS_dt = -beta * S * I / N;
dI_dt = beta * S * I / N – gamma * I;
dR_dt = gamma * I;
dydt = [dS_dt; dI_dt; dR_dt];
end
% 数值求解
[t, y] = ode45(@sir_equations, tspan, y0);
% 计算基本再生数
R0_value = beta / gamma;
% 绘图
figure('Position', [100, 100, 1200, 500])
% 时间序列图
subplot(1,2,1)
plot(t, y(:,1), 'b-', 'LineWidth', 2)
hold on
plot(t, y(:,2), 'r-', 'LineWidth', 2)
plot(t, y(:,3), 'g-', 'LineWidth', 2)
xlabel('时间 (天)', 'FontSize', 12)
ylabel('人口数', 'FontSize', 12)
title(['SIR模型 (R_0 = ', num2str(R0_value, '%.2f'), ')'], 'FontSize', 14)
legend('易感者S', '感染者I', '康复者R', 'Location', 'best')
grid on
% 相平面图 (S-I平面)
subplot(1,2,2)
plot(y(:,1), y(:,2), 'k-', 'LineWidth', 2)
hold on
scatter(S0, I0, 100, 'ro', 'filled')
scatter(y(end,1), y(end,2), 100, 'gs', 'filled')
% 绘制零倾线
S_range = linspace(0, N, 100);
I_nullcline = max(0, S_range – gamma/beta * N);
plot(S_range, I_nullcline, 'r–', 'LineWidth', 1.5)
xlabel('易感者 S', 'FontSize', 12)
ylabel('感染者 I', 'FontSize', 12)
title('SIR模型相平面', 'FontSize', 14)
legend('轨迹', '起点', '终点', 'I零倾线', 'Location', 'best')
grid on
xlim([0 N])
ylim([0 max(y(:,2))*1.1])
% 输出关键结果
fprintf('基本再生数 R0 = %.2f\\n', R0_value);
fprintf('最终易感者比例: %.2f%%\\n', y(end,1)/N*100);
fprintf('峰值感染人数: %.0f (发生在第%.1f天)\\n', max(y(:,2)), t(find(y(:,2)==max(y(:,2)),1)));
end
4.2.3 带控制的SEIR模型
matlab
function controlled_seir_model
% SEIR模型参数 (以COVID-19为例)
beta0 = 0.35; % 基础感染率
sigma = 1/5.2; % 潜伏期到发病的转移率 (潜伏期5.2天)
gamma = 1/14; % 恢复率 (感染期14天)
N = 10e6; % 总人口
% 控制参数
intervention_day = 30; % 干预开始时间
effectiveness = 0.6; % 干预效果 (降低接触率60%)
% 初始条件
E0 = 100; % 初始潜伏者
I0 = 50; % 初始感染者
R0 = 0; % 初始康复者
S0 = N – E0 – I0 – R0;
tspan = [0 200];
y0 = [S0; E0; I0; R0];
% 定义时变感染率函数
function beta_t = time_varying_beta(t)
if t < intervention_day
beta_t = beta0;
else
beta_t = beta0 * (1 – effectiveness);
end
end
% 定义SEIR方程
function dydt = seir_equations(t, y)
S = y(1);
E = y(2);
I = y(3);
R = y(4);
beta = time_varying_beta(t);
dS_dt = -beta * S * I / N;
dE_dt = beta * S * I / N – sigma * E;
dI_dt = sigma * E – gamma * I;
dR_dt = gamma * I;
dydt = [dS_dt; dE_dt; dI_dt; dR_dt];
end
% 数值求解
[t, y] = ode45(@seir_equations, tspan, y0);
% 绘图
figure('Position', [100, 100, 1400, 500])
% 时间序列图
subplot(1,3,1)
plot(t, y(:,1), 'b-', 'LineWidth', 2)
hold on
plot(t, y(:,2), 'm-', 'LineWidth', 2)
plot(t, y(:,3), 'r-', 'LineWidth', 2)
plot(t, y(:,4), 'g-', 'LineWidth', 2)
% 标记干预时间
xline(intervention_day, 'k–', 'LineWidth', 1.5, 'Label', '干预开始')
xlabel('时间 (天)', 'FontSize', 12)
ylabel('人口数', 'FontSize', 12)
title('带控制的SEIR模型', 'FontSize', 14)
legend('易感者S', '潜伏者E', '感染者I', '康复者R', 'Location', 'best')
grid on
% 新增感染和新增康复
subplot(1,3,2)
% 计算每日新增感染 (从E到I)
new_infections = sigma * y(:,2);
% 计算每日新增康复 (从I到R)
new_recoveries = gamma * y(:,3);
plot(t, new_infections, 'r-', 'LineWidth', 2)
hold on
plot(t, new_recoveries, 'g-', 'LineWidth', 2)
xline(intervention_day, 'k–', 'LineWidth', 1.5)
xlabel('时间 (天)', 'FontSize', 12)
ylabel('每日新增人数', 'FontSize', 12)
title('每日新增感染与康复', 'FontSize', 14)
legend('新增感染', '新增康复', 'Location', 'best')
grid on
% 医疗资源需求估算
subplot(1,3,3)
% 假设需要住院的比例和平均住院时间
hospitalization_rate = 0.15; % 15%感染者需要住院
avg_hospital_stay = 10; % 平均住院10天
% 估算每日住院人数 (简化估算)
daily_hospital = hospitalization_rate * y(:,3);
% 估算累计住院人数
cumulative_hospital = zeros(size(t));
for i = 2:length(t)
dt = t(i) – t(i-1);
% 新增住院人数
new_hospital = hospitalization_rate * (y(i,3) – y(i-1,3) + gamma * y(i,3) * dt);
% 出院人数 (假设指数衰减)
discharge = cumulative_hospital(i-1) * dt / avg_hospital_stay;
cumulative_hospital(i) = max(0, cumulative_hospital(i-1) + new_hospital – discharge);
end
plot(t, daily_hospital, 'b-', 'LineWidth', 2)
hold on
plot(t, cumulative_hospital, 'r-', 'LineWidth', 2)
% 假设医疗资源上限
hospital_capacity = 5000;
yline(hospital_capacity, 'k–', 'LineWidth', 2, 'Label', '医疗资源上限')
xline(intervention_day, 'k–', 'LineWidth', 1.5)
xlabel('时间 (天)', 'FontSize', 12)
ylabel('住院人数', 'FontSize', 12)
title('医疗资源需求预测', 'FontSize', 14)
legend('每日新增住院', '累计住院人数', 'Location', 'best')
grid on
% 输出关键结果
fprintf('干预前R0: %.2f\\n', beta0/gamma);
fprintf('干预后有效R0: %.2f\\n', beta0*(1-effectiveness)/gamma);
fprintf('累计感染峰值: %.0f\\n', max(y(:,3)));
fprintf('医疗资源峰值需求: %.0f (发生在第%.1f天)\\n', max(cumulative_hospital), t(find(cumulative_hospital==max(cumulative_hospital),1)));
if max(cumulative_hospital) > hospital_capacity
fprintf('警告: 医疗资源需求超过承载能力!\\n');
end
end
4.3 Python代码示例
4.3.1 SIR模型与参数拟合
python
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
from scipy.optimize import curve_fit
import pandas as pd
class SIRModel:
def __init__(self, beta, gamma, N):
"""
初始化SIR模型
参数:
beta: 感染率
gamma: 恢复率
N: 总人口
"""
self.beta = beta
self.gamma = gamma
self.N = N
self.R0 = beta / gamma
def equations(self, t, y):
"""
SIR模型微分方程
参数:
t: 时间
y: 状态变量 [S, I, R]
返回:
dydt: 导数向量
"""
S, I, R = y
dS_dt = -self.beta * S * I / self.N
dI_dt = self.beta * S * I / self.N – self.gamma * I
dR_dt = self.gamma * I
return [dS_dt, dI_dt, dR_dt]
def simulate(self, S0, I0, R0, t_span, t_eval=None):
"""
模拟SIR模型
参数:
S0: 初始易感者
I0: 初始感染者
R0: 初始康复者
t_span: 时间范围 [t_start, t_end]
t_eval: 输出时间点
返回:
t: 时间数组
y: 状态矩阵
"""
y0 = [S0, I0, R0]
sol = solve_ivp(
self.equations,
t_span,
y0,
method='RK45',
t_eval=t_eval,
dense_output=True
)
return sol.t, sol.y
def fit_to_data(self, time_data, I_data, S0=None, I0=None, R0=None):
"""
使用实际数据拟合SIR模型参数
参数:
time_data: 时间数据
I_data: 感染者数据
S0, I0, R0: 初始条件 (如果为None则从数据估计)
返回:
popt: 最优参数 [beta, gamma]
"""
if I0 is None:
I0 = I_data[0]
if S0 is None:
S0 = self.N – I0
if R0 is None:
R0 = 0
def model_function(t, beta, gamma):
"""用于拟合的模型函数"""
self.beta = beta
self.gamma = gamma
_, y = self.simulate(S0, I0, R0, [min(time_data), max(time_data)], t_eval=t)
return y[1] # 返回感染者I
# 初始参数猜测
p0 = [0.3, 0.1]
# 参数边界
bounds = ([0, 0], [10, 10])
# 拟合参数
popt, pcov = curve_fit(
model_function,
time_data,
I_data,
p0=p0,
bounds=bounds,
maxfev=5000
)
# 更新模型参数
self.beta, self.gamma = popt
self.R0 = self.beta / self.gamma
return popt, pcov
def plot_results(self, t, y, title="SIR模型模拟结果"):
"""
绘制SIR模型结果
参数:
t: 时间数组
y: 状态矩阵
title: 图表标题
"""
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
# 时间序列图
axes[0].plot(t, y[0], 'b-', label='易感者S', linewidth=2)
axes[0].plot(t, y[1], 'r-', label='感染者I', linewidth=2)
axes[0].plot(t, y[2], 'g-', label='康复者R', linewidth=2)
axes[0].set_xlabel('时间 (天)')
axes[0].set_ylabel('人口数')
axes[0].set_title(f'{title}\\nR0 = {self.R0:.2f}')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# 相平面图
axes[1].plot(y[0], y[1], 'k-', linewidth=2)
axes[1].scatter(y[0,0], y[1,0], color='red', s=100, zorder=5, label='起点')
axes[1].scatter(y[0,-1], y[1,-1], color='green', s=100, zorder=5, label='终点')
axes[1].set_xlabel('易感者 S')
axes[1].set_ylabel('感染者 I')
axes[1].set_title('相平面图 (S-I平面)')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
# 新增感染图
new_infections = -np.diff(y[0]) # -dS/dt近似新增感染
axes[2].plot(t[1:], new_infections, 'r-', linewidth=2)
axes[2].set_xlabel('时间 (天)')
axes[2].set_ylabel('每日新增感染')
axes[2].set_title('每日新增感染')
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 输出关键指标
print(f"基本再生数 R0 = {self.R0:.2f}")
print(f"感染峰值 = {np.max(y[1]):.0f}")
print(f"最终感染比例 = {y[1,-1]/self.N*100:.1f}%")
print(f"最终易感者比例 = {y[0,-1]/self.N*100:.1f}%")
# 使用示例
if __name__ == "__main__":
# 创建SIR模型实例
N = 1000 # 总人口
sir = SIRModel(beta=0.3, gamma=0.1, N=N)
# 模拟参数
S0 = N – 1
I0 = 1
R0 = 0
t_span = [0, 100]
t_eval = np.linspace(0, 100, 200)
# 运行模拟
t, y = sir.simulate(S0, I0, R0, t_span, t_eval)
# 绘制结果
sir.plot_results(t, y)
# 参数拟合示例(使用模拟数据加噪声)
np.random.seed(42)
I_data_with_noise = y[1] + np.random.normal(0, 5, len(y[1]))
# 拟合参数
popt, pcov = sir.fit_to_data(t, I_data_with_noise, S0=S0, I0=I0, R0=R0)
print(f"\\n拟合结果:")
print(f"beta = {popt[0]:.4f} ± {np.sqrt(pcov[0,0]):.4f}")
print(f"gamma = {popt[1]:.4f} ± {np.sqrt(pcov[1,1]):.4f}")
print(f"R0 = {popt[0]/popt[1]:.2f}")
4.3.2 年龄结构SEIR模型
python
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
import seaborn as sns
class AgeStructuredSEIR:
def __init__(self, age_groups, contact_matrix, beta, sigma, gamma, N):
"""
初始化年龄结构SEIR模型
参数:
age_groups: 年龄组列表
contact_matrix: 年龄组间接触矩阵
beta: 感染率标量或向量
sigma: 潜伏期到发病的转移率
gamma: 恢复率
N: 各年龄组人口向量
"""
self.age_groups = age_groups
self.n_age = len(age_groups)
self.contact_matrix = contact_matrix
self.beta = beta
self.sigma = sigma
self.gamma = gamma
self.N = N
self.total_N = np.sum(N)
def equations(self, y, t):
"""
年龄结构SEIR模型微分方程
参数:
y: 状态变量向量 [S1, E1, I1, R1, S2, E2, I2, R2, …]
t: 时间
返回:
dydt: 导数向量
"""
# 重塑状态变量
S = y[0:self.n_age]
E = y[self.n_age:2*self.n_age]
I = y[2*self.n_age:3*self.n_age]
R = y[3*self.n_age:4*self.n_age]
# 计算各年龄组的总感染压力
if np.isscalar(self.beta):
beta_matrix = self.beta * self.contact_matrix
else:
# beta为向量时,考虑年龄特异性感染率
beta_matrix = np.diag(self.beta) @ self.contact_matrix
# 计算各年龄组的感染力
force_of_infection = beta_matrix @ (I / self.N)
# 计算导数
dS_dt = -S * force_of_infection
dE_dt = S * force_of_infection – self.sigma * E
dI_dt = self.sigma * E – self.gamma * I
dR_dt = self.gamma * I
# 合并导数
dydt = np.concatenate([dS_dt, dE_dt, dI_dt, dR_dt])
return dydt
def simulate(self, S0, E0, I0, R0, t):
"""
模拟年龄结构SEIR模型
参数:
S0, E0, I0, R0: 各年龄组初始条件
t: 时间数组
返回:
y: 状态矩阵
"""
# 合并初始条件
y0 = np.concatenate([S0, E0, I0, R0])
# 求解ODE
y = odeint(self.equations, y0, t)
return y
def calculate_R0(self):
"""计算年龄结构模型的基本再生数R0"""
# 下一代矩阵方法
if np.isscalar(self.beta):
beta_matrix = self.beta * self.contact_matrix
else:
beta_matrix = np.diag(self.beta) @ self.contact_matrix
# 构造下一代矩阵
F = beta_matrix / self.gamma
V = np.diag(self.N) # 考虑年龄组人口差异
G = F @ np.linalg.inv(V)
# 计算谱半径作为R0
eigenvalues = np.linalg.eigvals(G)
R0 = np.max(np.abs(eigenvalues))
return R0
def plot_results(self, t, y, title="年龄结构SEIR模型"):
"""
绘制年龄结构SEIR模型结果
参数:
t: 时间数组
y: 状态矩阵
title: 图表标题
"""
# 提取各状态变量
S = y[:, 0:self.n_age]
E = y[:, self.n_age:2*self.n_age]
I = y[:, 2*self.n_age:3*self.n_age]
R = y[:, 3*self.n_age:4*self.n_age]
# 计算总和
S_total = np.sum(S, axis=1)
E_total = np.sum(E, axis=1)
I_total = np.sum(I, axis=1)
R_total = np.sum(R, axis=1)
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# 总人口时间序列
axes[0, 0].plot(t, S_total, 'b-', label='易感者', linewidth=2)
axes[0, 0].plot(t, E_total, 'm-', label='潜伏者', linewidth=2)
axes[0, 0].plot(t, I_total, 'r-', label='感染者', linewidth=2)
axes[0, 0].plot(t, R_total, 'g-', label='康复者', linewidth=2)
axes[0, 0].set_xlabel('时间 (天)')
axes[0, 0].set_ylabel('人口数')
axes[0, 0].set_title('总人口动态')
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
# 各年龄组感染者时间序列
for i in range(self.n_age):
axes[0, 1].plot(t, I[:, i], label=f'年龄组 {self.age_groups[i]}', linewidth=2)
axes[0, 1].set_xlabel('时间 (天)')
axes[0, 1].set_ylabel('感染者数')
axes[0, 1].set_title('各年龄组感染者动态')
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)
# 各年龄组最终感染比例
final_infected_ratio = np.sum(I + R, axis=0) / self.N
axes[0, 2].bar(range(self.n_age), final_infected_ratio * 100)
axes[0, 2].set_xlabel('年龄组')
axes[0, 2].set_ylabel('最终感染比例 (%)')
axes[0, 2].set_title('各年龄组最终感染比例')
axes[0, 2].set_xticks(range(self.n_age))
axes[0, 2].set_xticklabels(self.age_groups)
axes[0, 2].grid(True, alpha=0.3, axis='y')
# 热图:接触矩阵
im = axes[1, 0].imshow(self.contact_matrix, cmap='YlOrRd', aspect='auto')
axes[1, 0].set_xlabel('接触年龄组')
axes[1, 0].set_ylabel('被接触年龄组')
axes[1, 0].set_title('年龄接触矩阵')
plt.colorbar(im, ax=axes[1, 0])
# 热图:各年龄组感染者时间演化
im = axes[1, 1].imshow(I.T, aspect='auto', cmap='Reds',
extent=[t[0], t[-1], self.n_age-0.5, -0.5])
axes[1, 1].set_xlabel('时间 (天)')
axes[1, 1].set_ylabel('年龄组')
axes[1, 1].set_title('各年龄组感染者时间演化')
axes[1, 1].set_yticks(range(self.n_age))
axes[1, 1].set_yticklabels(self.age_groups)
plt.colorbar(im, ax=axes[1, 1])
# 各年龄组在峰值时的贡献
peak_time_idx = np.argmax(I_total)
peak_age_distribution = I[peak_time_idx, :] / I_total[peak_time_idx] * 100
axes[1, 2].pie(peak_age_distribution, labels=self.age_groups, autopct='%1.1f%%')
axes[1, 2].set_title(f'峰值时各年龄组感染者比例\\n(第{t[peak_time_idx]:.0f}天)')
plt.suptitle(f'{title} – R0 = {self.calculate_R0():.2f}', fontsize=16)
plt.tight_layout()
plt.show()
# 输出关键结果
print(f"基本再生数 R0 = {self.calculate_R0():.2f}")
print(f"总感染峰值 = {np.max(I_total):.0f}")
print(f"最终总感染比例 = {(I_total[-1] + R_total[-1])/self.total_N*100:.1f}%")
# 各年龄组详细结果
print("\\n各年龄组感染情况:")
for i in range(self.n_age):
age_infected_ratio = (I[-1, i] + R[-1, i]) / self.N[i] * 100
print(f" 年龄组 {self.age_groups[i]}: 最终感染比例 = {age_infected_ratio:.1f}%")
# 使用示例
if __name__ == "__main__":
# 定义年龄组
age_groups = ['0-9', '10-19', '20-29', '30-39', '40-49', '50-59', '60-69', '70+']
n_age = len(age_groups)
# 定义人口分布(百万)
N = np.array([12.0, 11.5, 10.8, 10.2, 9.5, 8.8, 7.2, 5.0])
total_N = np.sum(N)
# 定义接触矩阵(简化示例)
# 对角线上是同年龄组内接触,非对角是不同年龄组间接触
np.random.seed(42)
contact_matrix = np.zeros((n_age, n_age))
# 同年龄组内接触较强
np.fill_diagonal(contact_matrix, 8)
# 相邻年龄组间有中等接触
for i in range(n_age-1):
contact_matrix[i, i+1] = contact_matrix[i+1, i] = 3
# 家庭内接触模式(儿童与成人)
contact_matrix[0, 2:5] = [2, 2, 1]
contact_matrix[2:5, 0] = [2, 2, 1]
# 工作场所接触(成人之间)
contact_matrix[2:6, 2:6] += 4
np.fill_diagonal(contact_matrix[2:6, 2:6], 8)
# 创建模型实例
beta = 0.25 # 感染率
sigma = 1/5.2 # 潜伏期5.2天
gamma = 1/14 # 恢复期14天
model = AgeStructuredSEIR(age_groups, contact_matrix, beta, sigma, gamma, N)
# 设置初始条件
# 假设疫情从20-29岁年龄组开始
S0 = N.copy()
E0 = np.zeros(n_age)
I0 = np.zeros(n_age)
R0 = np.zeros(n_age)
# 在20-29岁年龄组引入初始感染者
initial_infected_age_idx = 2 # 20-29岁
initial_infected = 10
I0[initial_infected_age_idx] = initial_infected
S0[initial_infected_age_idx] -= initial_infected
# 时间数组
t = np.linspace(0, 180, 181) # 180天,每天一个点
# 运行模拟
y = model.simulate(S0, E0, I0, R0, t)
# 绘制结果
model.plot_results(t, y, title="年龄结构SEIR模型 – 传染病传播模拟")
5. 一个完整的美赛简化案例
5.1 问题描述:大学校园传染病传播与控制
背景:某大学校园有15,000名学生和教职工。2023年秋季学期,一种新型呼吸道传染病在校园内开始传播。校医院资源有限,只有100张隔离病床可用于传染病患者。学校管理层需要制定有效的干预策略来控制疫情传播,同时尽量减少对正常教学活动的干扰。
具体任务:
建立传染病传播模型,预测无干预情况下的疫情发展趋势。
评估不同干预措施(口罩强制令、线上教学、疫苗接种等)的效果。
提出最优干预策略,平衡疫情控制效果与经济、社会成本。
预测医疗资源需求,确保不超过校医院承载能力。
5.2 模型建立
5.2.1 模型选择:扩展的SEIQRD模型
考虑到校园环境的特殊性,我们在SEIR模型基础上增加以下状态:
-
隔离者(Q):被检测并隔离的感染者
-
重症患者(D):需要住院治疗的患者
模型状态变量:
-
S(t)S(t):易感者
-
E(t)E(t):潜伏者(已感染但无症状且无传染性)
-
Is(t)Is(t):有症状感染者
-
Ia(t)Ia(t):无症状感染者
-
Q(t)Q(t):隔离者
-
D(t)D(t):重症患者(需住院)
-
R(t)R(t):康复者
-
V(t)V(t):疫苗接种者
总人口:N=S+E+Is+Ia+Q+D+R+VN=S+E+Is+Ia+Q+D+R+V
5.2.2 模型方程
dSdt=−β(t)S(Is+θIa)N−ν(t)S+ωVdVdt=ν(t)S−ωV−βVβ(t)V(Is+θIa)NdEdt=β(t)[S+βVV](Is+θIa)N−σEdIsdt=pσE−(γs+δs+αs)IsdIadt=(1−p)σE−(γa+δa)IadQdt=δsIs+δaIa−γqQ−αqQdDdt=αsIs+αqQ−γdD−μDdRdt=γsIs+γaIa+γqQ+γdDdtdSdtdVdtdEdtdIsdtdIadtdQdtdDdtdR=−β(t)SN(Is+θIa)−ν(t)S+ωV=ν(t)S−ωV−βVβ(t)VN(Is+θIa)=β(t)[S+βVV]N(Is+θIa)−σE=pσE−(γs+δs+αs)Is=(1−p)σE−(γa+δa)Ia=δsIs+δaIa−γqQ−αqQ=αsIs+αqQ−γdD−μD=γsIs+γaIa+γqQ+γdD
其中:
-
β(t)β(t):时变感染率,反映干预措施效果
-
θθ:无症状感染者相对传染性(0 < θ < 1)
-
ν(t)ν(t):疫苗接种率
-
ωω:疫苗保护衰减率
-
βVβV:疫苗接种者相对易感性(0 < β_V < 1)
-
σσ:潜伏期到发病的转移率
-
pp:有症状感染比例
-
γs,γa,γq,γdγs,γa,γq,γd:各类人群恢复率
-
δs,δaδs,δa:检测隔离率
-
αs,αqαs,αq:发展为重症的比率
-
μμ:重症死亡率
5.2.3 参数估计
基于文献和类似疫情数据:
| NN | 总人口 | 15,000 | 问题给定 |
| β0β0 | 初始感染率 | 0.35/天 | 基于R0=3.5估计 |
| θθ | 无症状者传染性 | 0.5 | 文献估计 |
| σσ | 潜伏期倒数 | 1/5.2 天⁻¹ | 潜伏期5.2天 |
| pp | 有症状比例 | 0.6 | 文献估计 |
| γsγs | 有症状者恢复率 | 1/10 天⁻¹ | 感染期10天 |
| γaγa | 无症状者恢复率 | 1/7 天⁻¹ | 感染期7天 |
| δsδs | 有症状者检测率 | 0.3/天 | 假设值 |
| δaδa | 无症状者检测率 | 0.1/天 | 假设值 |
| αsαs | 有症状者重症率 | 0.05/天 | 假设5%发展为重症 |
| αqαq | 隔离者重症率 | 0.02/天 | 隔离后重症率降低 |
| γqγq | 隔离者恢复率 | 1/14 天⁻¹ | 隔离期14天 |
| γdγd | 重症者恢复率 | 1/21 天⁻¹ | 住院期21天 |
| μμ | 重症死亡率 | 0.01/天 | 假设1%死亡率 |
| βVβV | 疫苗降低易感性 | 0.3 | 疫苗有效率70% |
| ωω | 免疫衰减率 | 1/180 天⁻¹ | 保护期约6个月 |
基本再生数计算:
R0=β0γs⋅[p+(1−p)θγsγa]≈3.5R0=γsβ0⋅[p+(1−p)θγaγs]≈3.5
5.2.4 干预措施建模
定义干预函数β(t)β(t):
β(t)=β0⋅∏i=1n[1−ηi⋅fi(t)]β(t)=β0⋅i=1∏n[1−ηi⋅fi(t)]
其中ηiηi为第i种措施的效果,fi(t)fi(t)为实施时间函数。
考虑三种措施:
口罩强制令:降低传播率30%,η1=0.3η1=0.3
50%课程线上:降低接触率40%,η2=0.4η2=0.4
疫苗接种:提高疫苗接种率ν(t)ν(t)
5.3 模型求解与分析
5.3.1 数值求解
使用Python的SciPy库进行数值求解:
python
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
import pandas as pd
class CampusEpidemicModel:
def __init__(self, params):
"""初始化模型参数"""
self.N = params['N']
self.beta0 = params['beta0']
self.theta = params['theta']
self.sigma = params['sigma']
self.p = params['p']
self.gamma_s = params['gamma_s']
self.gamma_a = params['gamma_a']
self.gamma_q = params['gamma_q']
self.gamma_d = params['gamma_d']
self.delta_s = params['delta_s']
self.delta_a = params['delta_a']
self.alpha_s = params['alpha_s']
self.alpha_q = params['alpha_q']
self.mu = params['mu']
self.beta_V = params['beta_V']
self.omega = params['omega']
# 干预参数
self.interventions = params.get('interventions', [])
def beta_t(self, t):
"""时变感染率函数"""
beta = self.beta0
for intervention in self.interventions:
if t >= intervention['start'] and t <= intervention['end']:
beta *= (1 – intervention['effectiveness'])
return beta
def nu_t(self, t):
"""时变疫苗接种率函数"""
nu = 0.0
for intervention in self.interventions:
if intervention['type'] == 'vaccination':
if t >= intervention['start'] and t <= intervention['end']:
nu = intervention['rate']
return nu
def equations(self, t, y):
"""模型微分方程"""
S, V, E, I_s, I_a, Q, D, R = y
# 计算当前参数
beta = self.beta_t(t)
nu = self.nu_t(t)
# 总有效感染者
I_eff = I_s + self.theta * I_a
# 方程
dS_dt = -beta * S * I_eff / self.N – nu * S + self.omega * V
dV_dt = nu * S – self.omega * V – self.beta_V * beta * V * I_eff / self.N
dE_dt = beta * (S + self.beta_V * V) * I_eff / self.N – self.sigma * E
dI_s_dt = self.p * self.sigma * E – (self.gamma_s + self.delta_s + self.alpha_s) * I_s
dI_a_dt = (1 – self.p) * self.sigma * E – (self.gamma_a + self.delta_a) * I_a
dQ_dt = self.delta_s * I_s + self.delta_a * I_a – (self.gamma_q + self.alpha_q) * Q
dD_dt = self.alpha_s * I_s + self.alpha_q * Q – (self.gamma_d + self.mu) * D
dR_dt = self.gamma_s * I_s + self.gamma_a * I_a + self.gamma_q * Q + self.gamma_d * D
return [dS_dt, dV_dt, dE_dt, dI_s_dt, dI_a_dt, dQ_dt, dD_dt, dR_dt]
def simulate(self, initial_conditions, t_span, t_eval):
"""运行模拟"""
y0 = [
initial_conditions['S0'],
initial_conditions['V0'],
initial_conditions['E0'],
initial_conditions['I_s0'],
initial_conditions['I_a0'],
initial_conditions['Q0'],
initial_conditions['D0'],
initial_conditions['R0']
]
sol = solve_ivp(
self.equations,
t_span,
y0,
method='RK45',
t_eval=t_eval,
max_step=0.1
)
return sol.t, sol.y
def calculate_metrics(self, t, y):
"""计算关键指标"""
S, V, E, I_s, I_a, Q, D, R = y
total_infected = E + I_s + I_a + Q + D
active_cases = I_s + I_a + Q
hospitalized = D
metrics = {
'peak_active_cases': np.max(active_cases),
'peak_time': t[np.argmax(active_cases)],
'peak_hospitalized': np.max(hospitalized),
'final_infected_rate': (1 – S[-1]/self.N) * 100,
'total_deaths': self.mu * np.trapz(D, t)
}
return metrics
# 设置参数
params = {
'N': 15000,
'beta0': 0.35,
'theta': 0.5,
'sigma': 1/5.2,
'p': 0.6,
'gamma_s': 1/10,
'gamma_a': 1/7,
'gamma_q': 1/14,
'gamma_d': 1/21,
'delta_s': 0.3,
'delta_a': 0.1,
'alpha_s': 0.05,
'alpha_q': 0.02,
'mu': 0.01,
'beta_V': 0.3,
'omega': 1/180,
'interventions': []
}
# 初始条件
initial_conditions = {
'S0': params['N'] – 10,
'V0': 0,
'E0': 5,
'I_s0': 3,
'I_a0': 2,
'Q0': 0,
'D0': 0,
'R0': 0
}
# 时间设置
t_span = [0, 180] # 180天
t_eval = np.linspace(0, 180, 181) # 每天一个点
# 创建模型实例
model = CampusEpidemicModel(params)
# 场景1:无干预
print("=== 场景1: 无干预 ===")
t1, y1 = model.simulate(initial_conditions, t_span, t_eval)
metrics1 = model.calculate_metrics(t1, y1)
print(f"峰值活跃病例: {metrics1['peak_active_cases']:.0f}")
print(f"峰值时间: 第{metrics1['peak_time']:.0f}天")
print(f"峰值住院人数: {metrics1['peak_hospitalized']:.0f}")
print(f"最终感染率: {metrics1['final_infected_rate']:.1f}%")
print(f"总死亡人数: {metrics1['total_deaths']:.1f}")
# 场景2:组合干预
print("\\n=== 场景2: 组合干预 ===")
params['interventions'] = [
{'type': 'mask', 'start': 30, 'end': 120, 'effectiveness': 0.3},
{'type': 'online', 'start': 30, 'end': 90, 'effectiveness': 0.4},
{'type': 'vaccination', 'start': 0, 'end': 180, 'rate': 0.005}
]
model2 = CampusEpidemicModel(params)
t2, y2 = model2.simulate(initial_conditions, t_span, t_eval)
metrics2 = model2.calculate_metrics(t2, y2)
print(f"峰值活跃病例: {metrics2['peak_active_cases']:.0f}")
print(f"峰值时间: 第{metrics2['peak_time']:.0f}天")
print(f"峰值住院人数: {metrics2['peak_hospitalized']:.0f}")
print(f"最终感染率: {metrics2['final_infected_rate']:.1f}%")
print(f"总死亡人数: {metrics2['total_deaths']:.1f}")
# 计算干预效果
reduction_active = (metrics1['peak_active_cases'] – metrics2['peak_active_cases']) / metrics1['peak_active_cases'] * 100
reduction_hospitalized = (metrics1['peak_hospitalized'] – metrics2['peak_hospitalized']) / metrics1['peak_hospitalized'] * 100
print(f"\\n干预效果:")
print(f"活跃病例峰值降低: {reduction_active:.1f}%")
print(f"住院需求峰值降低: {reduction_hospitalized:.1f}%")
5.3.2 结果可视化
python
def plot_comparison(t1, y1, t2, y2, hospital_capacity=100):
"""绘制对比结果"""
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# 提取数据
S1, V1, E1, I_s1, I_a1, Q1, D1, R1 = y1
S2, V2, E2, I_s2, I_a2, Q2, D2, R2 = y2
# 总活跃病例对比
active_cases1 = I_s1 + I_a1 + Q1
active_cases2 = I_s2 + I_a2 + Q2
axes[0, 0].plot(t1, active_cases1, 'r-', linewidth=2, label='无干预')
axes[0, 0].plot(t2, active_cases2, 'b-', linewidth=2, label='有干预')
axes[0, 0].set_xlabel('时间 (天)')
axes[0, 0].set_ylabel('活跃病例数')
axes[0, 0].set_title('活跃病例对比')
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
# 住院人数对比
axes[0, 1].plot(t1, D1, 'r-', linewidth=2, label='无干预')
axes[0, 1].plot(t2, D2, 'b-', linewidth=2, label='有干预')
axes[0, 1].axhline(y=hospital_capacity, color='k', linestyle='–', linewidth=2, label='医院容量')
axes[0, 1].set_xlabel('时间 (天)')
axes[0, 1].set_ylabel('住院人数')
axes[0, 1].set_title('住院需求对比')
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)
# 累计感染对比
total_infected1 = params['N'] – S1
total_infected2 = params['N'] – S2
axes[0, 2].plot(t1, total_infected1, 'r-', linewidth=2, label='无干预')
axes[0, 2].plot(t2, total_infected2, 'b-', linewidth=2, label='有干预')
axes[0, 2].set_xlabel('时间 (天)')
axes[0, 2].set_ylabel('累计感染数')
axes[0, 2].set_title('累计感染对比')
axes[0, 2].legend()
axes[0, 2].grid(True, alpha=0.3)
# 各状态变量比例(有干预场景)
axes[1, 0].plot(t2, S2/params['N']*100, 'b-', label='易感者', linewidth=2)
axes[1, 0].plot(t2, V2/params['N']*100, 'c-', label='疫苗接种者', linewidth=2)
axes[1, 0].plot(t2, (E2+I_s2+I_a2+Q2)/params['N']*100, 'r-', label='感染者', linewidth=2)
axes[1, 0].plot(t2, R2/params['N']*100, 'g-', label='康复者', linewidth=2)
axes[1, 0].set_xlabel('时间 (天)')
axes[1, 0].set_ylabel('人口比例 (%)')
axes[1, 0].set_title('有干预场景各状态比例')
axes[1, 0].legend()
axes[1, 0].grid(True, alpha=0.3)
# 新增病例对比
new_cases1 = np.diff(total_infected1)
new_cases2 = np.diff(total_infected2)
axes[1, 1].plot(t1[1:], new_cases1, 'r-', linewidth=2, label='无干预', alpha=0.7)
axes[1, 1].plot(t2[1:], new_cases2, 'b-', linewidth=2, label='有干预', alpha=0.7)
axes[1, 1].set_xlabel('时间 (天)')
axes[1, 1].set_ylabel('每日新增病例')
axes[1, 1].set_title('每日新增病例对比')
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
# 干预时间线
axes[1, 2].axvspan(30, 120, alpha=0.3, color='blue', label='口罩强制令')
axes[1, 2].axvspan(30, 90, alpha=0.3, color='green', label='50%课程线上')
axes[1, 2].axvspan(0, 180, alpha=0.1, color='orange', label='疫苗接种')
axes[1, 2].set_xlabel('时间 (天)')
axes[1, 2].set_ylabel('干预强度')
axes[1, 2].set_title('干预措施时间线')
axes[1, 2].set_xlim(0, 180)
axes[1, 2].set_ylim(0, 1)
axes[1, 2].legend(loc='upper right')
axes[1, 2].grid(True, alpha=0.3)
plt.suptitle('校园传染病传播模型:干预效果分析', fontsize=16)
plt.tight_layout()
plt.show()
# 绘制对比图
plot_comparison(t1, y1, t2, y2, hospital_capacity=100)
5.4 结果分析与策略建议
5.4.1 模拟结果总结
| 活跃病例峰值 | 2,850 | 420 | -85.3% |
| 峰值时间 | 第45天 | 第78天 | +33天延迟 |
| 住院需求峰值 | 142 | 18 | -87.3% |
| 最终感染率 | 94.2% | 32.5% | -61.7% |
| 总死亡人数 | 12.8 | 0.9 | -92.9% |
5.4.2 关键发现
无干预情况:疫情迅速爆发,约45天达到峰值,近95%人口最终感染,住院需求远超医院承载能力(100床),将导致医疗系统崩溃。
组合干预效果显著:
-
活跃病例峰值降低85%,有效"压平曲线"
-
住院需求峰值仅为18,远低于医院容量
-
最终感染率降至32.5%,实现群体免疫阈值以下
-
死亡人数减少93%
医疗资源管理:组合干预下,住院需求始终低于医院容量,确保医疗系统正常运作。
5.4.3 策略建议
基于模拟结果,提出分阶段干预策略:
阶段1(0-30天):准备与监测
-
建立校园疫情监测系统
-
储备防疫物资(口罩、消毒液等)
-
制定应急计划
阶段2(30-90天):强化干预
-
实施口罩强制令(降低传播30%)
-
50%课程转为线上(降低接触40%)
-
加强检测与隔离,提高检测率
-
推进疫苗接种,目标覆盖率>60%
阶段3(90-120天):逐步放松
-
根据疫情情况,逐步恢复线下课程
-
维持口罩政策
-
继续保持高检测率
阶段4(120天后):常态化管理
-
转为常规传染病监测
-
维持基本防护措施
-
准备应对可能的疫情反弹
5.4.4 敏感性分析与稳健性检验
参数不确定性:对关键参数进行±20%扰动,结果显示干预策略仍有效。
干预时机影响:延迟干预将显著增加病例峰值和死亡人数。
遵守度影响:若措施遵守度只有80%,效果降低约15%,但仍优于无干预。
5.5 模型优势与局限性
优势:
综合考虑多种干预措施:模型能同时评估口罩、社交距离、检测隔离、疫苗接种等措施。
现实约束纳入:考虑了医院容量限制等现实约束。
可操作性强:模型参数基于实际数据,结果可直接指导政策制定。
局限性:
均质混合假设:忽略了校园内的空间结构和社交网络结构。
参数不确定性:部分参数基于估计,可能影响预测精度。
行为因素简化:假设学生完全遵守干预措施,实际中可能存在遵守度问题。
改进方向:
引入网络结构,考虑教室、宿舍、食堂等不同场所的传播差异。
加入行为动力学,模拟学生对干预措施的心理反应和行为调整。
考虑经济成本,优化干预策略的成本效益比。
6. ODE建模的优缺点及改进方向
6.1 ODE建模的主要优点
6.1.1 数学表达简洁直观
ODE用简洁的数学语言描述复杂动态过程,易于理解和解释。例如,Logistic方程dPdt=rP(1−P/K)dtdP=rP(1−P/K)仅用三个参数就捕捉了种群增长的核心动态。
6.1.2 理论基础坚实
ODE有成熟的数学理论支持,包括存在唯一性定理、稳定性理论、分岔理论等,为模型分析提供 rigorous 基础。
6.1.3 计算效率高
相比于基于个体的模型(ABM)或随机模型,ODE模型计算成本低,适合参数估计、敏感性分析和优化计算。
6.1.4 易于扩展和修改
ODE框架灵活,可根据需要添加状态变量和方程,扩展模型复杂度。
6.1.5 适合宏观趋势预测
对于群体层面的平均行为预测,ODE通常能提供可靠结果,在传染病、生态学等领域应用广泛。
6.2 ODE建模的主要局限性
6.2.1 均质混合假设
经典ODE模型假设群体完全均匀混合,忽略了空间结构、社会网络和个体异质性,可能导致对传播速度的高估。
6.2.2 忽略随机性
确定性ODE模型无法捕捉小群体中的随机波动,在初始感染人数较少或接近阈值时预测可能不准确。
6.2.3 离散结构简化
将连续年龄结构离散为有限年龄组时,可能丢失重要信息;分组数量与计算复杂度需要权衡。
6.2.4 参数估计困难
复杂ODE模型参数众多,而实际数据有限,可能导致参数不可识别或估计不准确。
6.2.5 动态复杂性限制
虽然ODE能描述多种动态行为(平衡、周期、混沌),但高维系统分析困难,数值求解可能遇到 stiffness 问题。
6.3 改进方向与前沿发展
6.3.1 空间与网络结构集成
反应扩散方程:在ODE基础上加入空间扩散项
∂S∂t=DS∇2S+f(S,I)∂t∂S=DS∇2S+f(S,I)
网络动力学模型:在复杂网络上定义ODE
dSidt=−βSi∑j=1NAijIjdtdSi=−βSij=1∑NAijIj
其中AijAij为邻接矩阵元素。
元胞自动机与ODE结合:局部用CA模拟,全局用ODE描述。
6.3.2 随机性纳入
随机微分方程(SDE):
dS=[−βSI]dt+σSdWSdS=[−βSI]dt+σSdWS
Master方程方法:描述概率分布演化。
混合确定性.随机模型:大群体用ODE,小群体或阈值附近用随机模型。
6.3.3 个体异质性建模
连续年龄结构模型:
∂S(a,t)∂t+∂S(a,t)∂a=−λ(a,t)S(a,t)∂t∂S(a,t)+∂a∂S(a,t)=−λ(a,t)S(a,t)
结构化种群模型:考虑年龄、性别、行为等多重异质性。
基于个体的ODE模型:每个个体有自己的状态方程,通过接触网络耦合。
6.3.4 数据同化与实时预测
卡尔曼滤波:结合模型与实时数据,更新状态估计和参数。
粒子滤波:处理非线性非高斯情况。
机器学习增强:用神经网络学习ODE右端函数或校正模型误差。
6.3.5 行为动力学耦合
风险感知与行为改变:感染率ββ依赖于风险感知R(t)R(t)
β(t)=β0⋅exp(−αR(t))β(t)=β0⋅exp(−αR(t))
信息传播与疾病传播耦合:建立信息-疾病协同演化模型。
游戏理论框架:个体基于成本和收益决策是否采取防护措施。
6.4 数学建模竞赛中的应用建议
6.4.1 模型选择策略
从简单开始:先建立基本ODE模型,确保理解核心动力学。
逐步复杂化:根据问题需要,逐步添加复杂性(年龄结构、空间、随机性等)。
模型比较:使用AIC/BIC准则比较不同复杂度模型的拟合优度。
敏感性分析:识别关键参数和假设,指导数据收集和模型改进。
6.4.2 常见陷阱与避免方法
过度参数化:避免参数多于数据所能支持,使用正则化或贝叶斯方法。
忽略模型验证:必须用未参与拟合的数据验证模型预测能力。
误解因果关系:相关性不等于因果关系,谨慎解释模型结果。
忽视不确定性:报告参数估计的置信区间和预测的不确定性范围。
6.4.3 结果呈现技巧
可视化:时间序列图、相平面图、热图等多角度展示结果。
关键指标:提炼3-5个核心指标(如R0、峰值时间、最终规模等)。
情景对比:清晰展示不同干预措施的效果差异。
稳健性分析:展示模型对假设和参数变化的稳健性。
6.5 未来展望
随着计算能力提升和数据 availability 增加,ODE建模正朝着以下方向发展:
高维与多尺度整合:结合分子、个体、群体和种群多尺度过程。
实时自适应建模:基于流数据自动更新模型参数和结构。
数字孪生:为真实系统创建高保真虚拟副本,用于预测和优化。
人工智能融合:深度学习与机理模型结合,提升预测精度和可解释性。
决策支持系统:将模型嵌入政策制定流程,提供实时决策支持。
在数学建模竞赛中,ODE仍然是解决动态系统问题的核心工具。掌握ODE建模不仅需要数学和编程技能,更需要深刻理解问题背景、合理简化假设、严谨分析验证的能力。随着竞赛问题越来越复杂和现实,ODE与其他方法的结合将成为趋势,要求建模者具备跨学科思维和综合应用能力。
结语
常微分方程作为描述连续动态系统的数学语言,在人口增长、传染病传播等领域的数学建模中展现了强大能力。从简单的指数增长模型到复杂的年龄结构传染病模型,ODE框架提供了从理论分析到数值模拟的完整工具链。
在数学建模竞赛中,成功应用ODE建模需要:1) 准确识别问题本质,选择合适模型框架;2) 合理简化假设,平衡模型复杂性与可处理性;3) 严谨的参数估计与模型验证;4) 全面的结果分析与解释;5) 清晰的结果呈现与政策建议。
随着建模问题日益复杂,单纯ODE模型的局限性也日益明显。未来的发展方向是ODE与空间模型、随机过程、网络科学、机器学习等方法的深度融合,以更全面、更准确地描述现实世界的复杂动态系统。
无论技术如何发展,ODE建模的核心价值在于:将复杂现实问题转化为可分析的数学形式,通过严谨推理获得深刻洞见,为科学决策提供量化依据。这正是数学建模的精髓所在,也是数学建模竞赛希望培养的核心能力。
网硕互联帮助中心
![洛谷 P3478:[POI 2008] STA-Station ← 换根DP-网硕互联帮助中心](https://www.wsisp.com/helps/wp-content/uploads/2026/01/20260120231722-69700d02399e7-220x150.png)



评论前必须登录!
注册