$ n; \3 g. W2 N0 E; Q5 V* H. ^$ ~传染病的数学模型是数学建模中的典型问题,标准名称是流行病的数学模型(Mathematical models of epidemic diseases)。建立传染病的数学模型来描述传染病的传播过程,研究传染病的传播速度、空间范围、传播途径、动力学机理等问题,以指导对传染病的有效地预防和控制,具有重要的现实意义。2 }2 y( ~2 p' C
& E# |$ }+ |+ n( C/ m- C* R ( r8 ]6 u3 ^" u" @不同类型传染病的传播具有不同的特点,传染病的传播模型不是从医学角度分析传染病的传播过程,而是按照传播机理建立不同的数学模型。6 g ~3 \! D- @( u* \2 J. }
9 w7 R+ k; @/ X( A a ~% C1 x$ ~+ _: V# N# `* y: R4 O
首先,把传染病流行范围内的人群分为 S、E、I、R 四类,具体含义如下: # Z% n8 f, [. u: x/ ~6 }5 k! W/ i! s Q
9 t1 y# I+ k& Q; d4 H2 S
S 类(Susceptible),易感者,指缺乏免疫能力的健康人,与感染者接触后容易受到感染;4 D" R1 T; f" Z0 U& h+ s4 O
9 Q: i9 ?# c2 H7 h: q0 A7 v# q; F; _
E 类(Exposed),暴露者,指接触过感染者但暂无传染性的人,适用于存在潜伏期的传染病;. E0 j( n$ D9 Y2 m' d1 m a
. q6 ~4 R: I% \
' q5 o( J e" A+ BI 类(Infectious),患病者,指具有传染性的患病者,可以传播给 S 类成员将其变为 E 类或 I 类成员; : q! t. i' S& P/ L% d - |: f9 h+ t, r- n, L - N0 h* d3 w5 k" P, I% `R 类(Recovered),康复者,指病愈后具有免疫力的人。如果免疫期有限,仍可以重新变为 S 类成员,进而被感染;如果是终身免疫,则不能再变为 S类、E类或 I 类成员。, `* _* k4 n6 }) i0 i5 F2 Q& ~
- z8 _; J" R- n* V 1 y: }, g, f' a5 \1 ]8 y常见的传染病模型按照传染病类型分为 SI、SIR、SIRS、SEIR 模型等,就是由以上四类人群根据不同传染病的特征进行组合而产生的不同模型。 4 N4 V7 d! S8 y) i# ?5 [3 Q$ s0 B9 t7 G) D5 Z9 _% ~
, K. g! ?' @0 v% }, `/ O
1 G* U; m- o+ z$ ^; M Y3 Z
: a4 K y, J1 k) u # Z+ A; U0 k5 K( g Z% P2 b 4 p* o# ^1 t) I& K" J / \, D" k6 ~) X9 z0 P, K2. 疫情传播 SI 模型) A2 P& O* _6 ^7 J: z
2.1 SI 模型的适用范围( E; m$ u8 \) M
SI 模型适用于只有易感者和患病者两类人群,且无法治愈的疾病,例如 T型病、僵尸。; w3 q4 K5 v. C
3 P. u; k+ H( `* T: b ) ]1 k4 q2 T. D* H2 n+ Q ' l( m; T! f. F A+ j' }# N" T- }6 S# j* N( h: ^9 L2 w% y
2.2 SI 模型的假设/ L. H* L9 [4 S# {
考察地区的总人数 N 不变,即不考虑生死或迁移;2 Z4 o: U; ]6 j- ?1 ^/ r* j7 V6 J
人群分为易感者(S类)和患病者(I类)两类;( ?. `+ s! w4 H: \
易感者(S类)与患病者(I类)有效接触即被感染,变为患病者,无潜伏期、无治愈情况、无免疫力;) G& y* W" [& A R5 _0 \
每个患病者每天有效接触的易感者的平均人数(日接触数)是 λ \lambdaλ,称为日接触率; ; N6 j7 F+ x8 E; v4 I将第 t 天时 S类、I 类人群的占比记为 s ( t ) s(t)s(t)、i ( t ) i(t)i(t),数量为 S ( t ) S(t)S(t)、I ( t ) I(t)I(t);初始日期 t = 0 t=0t=0 时, S类、I 类人群占比的初值为 s 0 s_0s 6 u/ c4 I) p+ H5 x5 U o0 3 B/ R" I9 b% K9 x ! e9 c- H7 z0 i; G# ^7 n
、i 0 i_0i - U" E/ ~+ R/ @( T! b7 Z4 ]
0$ u/ u2 J h) d2 T) s
( L, Y# U& J& h% s. {0 C 。 $ c4 ~* z8 @ o1 D1 ~+ q6 Z \3 n2.3 SI 模型的微分方程 " R) K6 c( ] h由9 B. m" [) c% Q% U$ x
N d i d t = N λ s i N\frac{di}{dt} = N\lambda s i5 A: |$ n v6 G j# e- I
N 3 m9 {) R+ D; E% kdt + |( A' f% E) d: ~# _di3 I* X" h4 g* x! U4 D3 _: k
$ F3 u6 \4 k% n* | =Nλsi& f5 \0 x% Z3 O1 R' Y
2 J$ o" ?- e0 Y( S- d3 N
" I' s% z8 c8 c. T
得: # l" K$ f& n$ m$ K( c$ T5 y& Cd i d t = λ i ( 1 − i ) , i ( 0 ) = i 0 \frac{di}{dt} = \lambda i (1-i),\ i(0) = i_0 . a, Y7 H) b3 w$ ~dt H( o1 v3 c1 Q$ X. r3 t
di" F, O$ ^7 R4 ^& E
! u( X+ J z# b. W
=λi(1−i), i(0)=i + G5 n4 A% W8 b9 [" {/ K' O7 m06 B' r, X- t S. K* Q6 ~7 \; a# T
2 r- O9 J" `! k$ K- s 3 V3 y, G) h9 [* m4 A( Q+ I* O( ^! u6 x8 E; w
7 H3 g0 Y& R/ k
这是 Logistic 模型,用分离变量法可以求出其解析解为: # ~7 H' `3 A/ q/ w; Ii ( t ) = 1 1 + ( 1 / i 0 − 1 ) e − λ t I ( t ) = N i ( t ) i(t)=\frac{1}{1+(1/i_0 - 1)\ e^{-\lambda t}}\\ I(t)= N\ i(t)9 y. O B, L" W
i(t)= 2 y9 \! z/ }" q( R- g
1+(1/i `2 n- d6 @0 P( E" O0* l$ E6 N$ p5 D' `) `* \; p
- s" p9 }# w& y! _/ m4 u- F0 l
−1) e 4 q4 z" r, |4 X* i: e−λt+ p6 g& W( E' x0 B7 W; q/ z
" u- L# R$ g, H n4 K1 - @( U: g+ |, j0 a - g$ L! P& z2 S- X3 J ) E2 x _' Q( b5 g# I: r( SI(t)=N i(t) ' o6 f7 m, L9 n; Y" z( r0 V3 l 3 V2 t. K; A; U! V- n1 g# ~5 Z2 k9 b7 N
9 `# Q# r' M5 U5 Y6 ^ ( A6 ~5 t0 @. C3. SI 模型的 Python 编程 . H6 p9 t* g8 `( }+ O3.1 SI 模型的解析解( ^4 X2 k |$ ]0 ]: X
上文已经得到 SI 模型的解析解,对此很容易通过 Python 编程实现,详见本文例程。 # D5 H0 k6 s' b; t ' k* M) h% e4 | u$ P3 l 2 m) K: S2 r W! F虽然 SI 模型的解析解并不复杂,而且解的精度当然是最好的,但我们仍然不鼓励用解析解的方法。原因在于,一是对于小白求解析解的过程相对复杂困难,而且可能出错,二是对于更复杂的模型是没有解析解的,即便大神也只能用数值方法求解。既然如此,不如从一开始就学习、掌握数值求解方法,熟悉数值解法的编程实现。 7 i& m% x3 _0 g& D5 Y" F8 b% _8 a6 ^3 B4 m2 U7 k/ k |5 e* X2 g) |
5 q) w7 H ?2 L* Y8 M7 g4 f! f3.2 SI 模型的数值解 2 z/ q# T1 W2 P. m7 C& N/ Y0 eSI 模型是常微分方程初值问题,可以使用 Scipy 工具包的 scipy.integrate.odeint() 函数求数值解,具体方法可以参考前文《Python小白的数学建模课-09 微分方程模型》。" r8 z6 i4 e' }' T" S
3 c! [6 [* X$ p
, g5 c- g' e; d- j4 f. ^6 Y. p
scipy.integrate.odeint(func, y0, t, args=())3 G+ Y8 A5 v. i4 o. y( Q" H
1; N2 Z* [- ^* Z% G
**scipy.integrate.odeint()**是求解微分方程的具体方法,通过数值积分来求解常微分方程组。 % r* F2 M! h, t2 y/ \* Y& ^9 g6 A9 p8 @ ! `1 j. ~ t/ H, }2 ? ) A7 i6 R/ k2 }: E1 ^1 {+ }odeint() 的主要参数: & |! p! Z! X* G& H) `8 T * _* V P3 Q, @, I# S0 [% k8 I E: I/ k) s
func: callable(y, t, …) 导数函数 f ( y , t ) f(y,t)f(y,t) ,即 y 在 t 处的导数,以函数的形式表示 ' K# ~% B/ w) C/ `9 `" C2 @y0: array: 初始条件 y 0 y_0y + [! z( W$ k* B) W0 F0 F1 s
0: r, X% d5 X- n, |0 R" P( _
$ I: S+ S4 [+ @7 Z) }: k' r: I- c ,对于常微分方程组 y 0 y_0y $ o/ T9 q( h4 u. {0+ R+ Z- Z- z) E+ }) v/ c
) x, [1 C$ D4 i1 c" o 则为数组向量( V# x3 y7 @; B" A5 [4 B
t: array: 求解函数值对应的时间点的序列。序列的第一个元素是与初始条件 y 0 y_0y : W( J& {3 g( N6 D
0 5 t$ s! N; O! @4 \9 S" r" y 1 P- L* B4 l1 X6 u8 m 对应的初始时间 t 0 t_0t 9 j: h5 I# i0 a
08 k% ? ^$ h' I+ Y0 X7 r o: F
5 A: \1 L/ Y) s9 O* o/ ]5 U
;时间序列必须是单调递增或单调递减的,允许重复值。 3 ~$ B: Y- K, c. q. cargs: 向导数函数 func 传递参数。当导数函数 f ( y , t , p 1 , p 2 , . . ) f(y,t,p1,p2,..)f(y,t,p1,p2,..) 包括可变参数 p1,p2… 时,通过 args =(p1,p2,…) 可以将参数p1,p2… 传递给导数函数 func。 & {& Z0 h' w$ B$ @- u$ m' Todeint() 的返回值:; x# I$ c, ~3 p9 t7 O; o
( j# S+ ?- W4 `: @4 B " d+ w2 D; H4 ~2 oy: array 数组,形状为 (len(t),len(y0),给出时间序列 t 中每个时刻的 y 值。9 ^4 z, d1 ^& p7 Y1 s k2 r, t
odeint() 的编程步骤: : s/ I( J( Q& Q S5 Q. I) C1 C
1 C4 L2 z- _! F7 C5 o导入 scipy、numpy、matplotlib 包;( l6 V' n; W! Z I8 h3 U! u
定义导数函数 f ( i , t ) = λ i ( 1 − i ) f(i,t)=\lambda i (1-i)f(i,t)=λi(1−i) ; 1 `$ L: P% Y! e3 D# q; f9 X$ |" l定义初值 i 0 i_0i 7 A4 |+ Y( g# K2 H& W
0 7 M( N3 _: M& h+ `0 q3 o % `) ^0 q. F% A$ @( [
和 i ii 的定义区间 [ t 0 , t ] [t_0,\ t][t 2 _, X2 j; m6 T- P; x0. `+ h* x: v) _6 Y1 b
- n# H$ Y( i' U. r+ S2 F8 X$ P , t];3 {; U1 D& R- q4 ]' Q! m1 C1 ?9 w
调用 odeint() 求 i ii 在定义区间 [ t 0 , t ] [t_0,\ t][t 7 \# m' d" l$ G+ n- M9 P4 Z- z. X
0: [# W9 l2 M7 H) `# d1 G, {$ K
0 ^. e. z8 n* ]2 e
, t] 的数值解。 5 g3 w: ] }& l% O8 F! n) m8 D 7 Q5 P! n6 Z7 y" S! V/ P: @9 Y $ k {6 A" `5 k) A }3.3 Python例程:SI 模型的解析解与数值解9 z* n2 b/ N9 ?7 O
# 1. SI 模型,常微分非常,解析解与数值解的比较 0 A+ m2 O( y, S+ Kfrom scipy.integrate import odeint # 导入 scipy.integrate 模块 : [, m# P) L! D5 X5 Rimport numpy as np # 导入 numpy包 : F2 a* l+ P: ?& `5 n7 `import matplotlib.pyplot as plt # 导入 matplotlib包 + i* A0 a/ `2 Q" r% v/ K( H/ z9 t8 @# Y0 j( X2 |4 z