SIR模型是流行病学中最经典的仓室模型,将人群划分为易感者(Susceptible)、感染者(Infected)和康复者(Recovered)三个 compartment,通过常微分方程组描述疾病在人群中的传播动态。本文用Java实现SIR模型,并对比欧拉法与四阶龙格-库塔法(RK4)的数值精度。
模型建立
微分方程组
设总人口 $N = S + I + R$ 恒定,传播参数为:
– $\beta$:感染率,每个感染者每天有效接触并传染的人数
– $\gamma$:康复率,每天康复人数占感染者的比例($1/\gamma$ 为平均感染周期)
$$
\begin{cases}
\frac{dS}{dt} = -\frac{\beta S I}{N} \
\frac{dI}{dt} = \frac{\beta S I}{N} – \gamma I \
\frac{dR}{dt} = \gamma I
\end{cases}
$$
基本再生数
$R_0 = \beta / \gamma$ 表示一个感染者在完全易感人群中平均能传染的人数。当 $R_0 > 1$ 时疫情扩散,$R_0 < 1$ 时疫情自然消退。
Java实现
/**
* SIR流行病学模型
* 支持欧拉法与四阶龙格-库塔法数值求解
*/
public class SIRModel {
// 模型参数
private final double beta; // 感染率
private final double gamma; // 康复率
private final double N; // 总人口
public SIRModel(double beta, double gamma, double N) {
this.beta = beta;
this.gamma = gamma;
this.N = N;
}
/** 计算 dS/dt */
public double dSdt(double S, double I) {
return -beta * S * I / N;
}
/** 计算 dI/dt */
public double dIdt(double S, double I) {
return beta * S * I / N - gamma * I;
}
/** 计算 dR/dt */
public double dRdt(double I) {
return gamma * I;
}
/**
* 欧拉法前向迭代
* @param S0, I0, R0 初始状态
* @param dt 时间步长
* @param steps 迭代步数
* @return 每一步的 [S, I, R] 数组
*/
public double[][] euler(double S0, double I0, double R0, double dt, int steps) {
double[][] result = new double[steps + 1][3];
double S = S0, I = I0, R = R0;
result[0] = new double[]{S, I, R};
for (int i = 1; i <= steps; i++) {
double sNew = S + dt * dSdt(S, I);
double iNew = I + dt * dIdt(S, I);
double rNew = R + dt * dRdt(I);
S = sNew; I = iNew; R = rNew;
result[i] = new double[]{S, I, R};
}
return result;
}
/**
* 四阶龙格-库塔法 (RK4)
* 精度为 O(dt⁴),远优于欧拉法的 O(dt)
*/
public double[][] rk4(double S0, double I0, double R0, double dt, int steps) {
double[][] result = new double[steps + 1][3];
double S = S0, I = I0, R = R0;
result[0] = new double[]{S, I, R};
for (int n = 1; n <= steps; n++) {
// k1
double k1S = dSdt(S, I);
double k1I = dIdt(S, I);
double k1R = dRdt(I);
// k2
double k2S = dSdt(S + 0.5 * dt * k1S, I + 0.5 * dt * k1I);
double k2I = dIdt(S + 0.5 * dt * k1S, I + 0.5 * dt * k1I);
double k2R = dRdt(I + 0.5 * dt * k1I);
// k3
double k3S = dSdt(S + 0.5 * dt * k2S, I + 0.5 * dt * k2I);
double k3I = dIdt(S + 0.5 * dt * k2S, I + 0.5 * dt * k2I);
double k3R = dRdt(I + 0.5 * dt * k2I);
// k4
double k4S = dSdt(S + dt * k3S, I + dt * k3I);
double k4I = dIdt(S + dt * k3S, I + dt * k3I);
double k4R = dRdt(I + dt * k3I);
S += dt / 6.0 * (k1S + 2 * k2S + 2 * k3S + k4S);
I += dt / 6.0 * (k1I + 2 * k2I + 2 * k3I + k4I);
R += dt / 6.0 * (k1R + 2 * k2R + 2 * k3R + k4R);
result[n] = new double[]{S, I, R};
}
return result;
}
/** 计算基本再生数 R₀ */
public double getR0() {
return beta / gamma;
}
/** 计算群体免疫阈值 */
public double herdImmunityThreshold() {
return 1.0 - 1.0 / getR0();
}
public static void main(String[] args) {
// 模拟参数:总人口100万,初始1人感染
// R0 = 2.5 (类似流感), 感染周期7天 -> gamma = 1/7
double N = 1_000_000;
double beta = 2.5 / 7.0; // R0 = beta / gamma = 2.5
double gamma = 1.0 / 7.0;
double S0 = N - 1, I0 = 1, R0_init = 0;
double dt = 0.1; // 天
int days = 200;
int steps = (int) (days / dt);
SIRModel model = new SIRModel(beta, gamma, N);
System.out.printf("基本再生数 R₀ = %.2f%n", model.getR0());
System.out.printf("群体免疫阈值 = %.1f%% %n%n", model.herdImmunityThreshold() * 100);
double[][] rk4Result = model.rk4(S0, I0, R0_init, dt, steps);
// 输出关键时间节点
System.out.println("天数\t\t易感者\t\t感染者\t\t康复者");
for (int d = 0; d <= days; d += 20) {
int idx = (int) (d / dt);
double[] state = rk4Result[idx];
System.out.printf("第%3d天\t%.0f\t\t%.0f\t\t%.0f%n", d, state[0], state[1], state[2]);
}
// 找到感染峰值
double maxI = 0;
int peakDay = 0;
for (int i = 0; i <= steps; i++) {
if (rk4Result[i][1] > maxI) {
maxI = rk4Result[i][1];
peakDay = (int) (i * dt);
}
}
System.out.printf("%n感染峰值出现在第 %d 天,约 %.0f 人同时感染%n", peakDay, maxI);
}
}
数值方法对比
| 方法 | 局部截断误差 | 全局误差 | 稳定性 | 适用场景 |
|---|---|---|---|---|
| 欧拉法 | $O(h^2)$ | $O(h)$ | 条件稳定 | 教学演示、实时粗略估计 |
| RK4 | $O(h^5)$ | $O(h^4)$ | 较稳定 | 高精度科学计算 |
对于SIR模型,RK4仅需欧拉法约 $1/100$ 的步长即可达到相同精度,在200天模拟中可将计算量从 $20000$ 步降至 $2000$ 步。
扩展:SEIR模型
在SIR基础上增加潜伏期 compartment (E, Exposed):
$$
\frac{dE}{dt} = \frac{\beta S I}{N} – \sigma E, \quad \frac{dI}{dt} = \sigma E – \gamma I
$$
其中 $\sigma$ 为潜伏期转阳率。SEIR更适用于具有明显潜伏期的新冠等传染病建模。
SIR模型虽然简化,但揭示了疫情传播的核心规律:控制 $R_0$ 是遏制疫情的唯一途径,而疫苗接种通过降低易感者比例实现群体免疫。