我也寫了個疫情傳播模擬程式
- 2020 年 2 月 19 日
- 筆記
前言
前一陣子看到有人製作了《疫情傳播模擬程式》,是用 Java
做的。裡面根據多種實際情況,如居民移動意願、醫護能力、病毒傳播能力,來模擬疫情的發展。看完之後,我暗暗稱奇,特別是結合一些影片和照片,確實做得非常好。
後來過了幾天,這個影片還上了 人民日報
的微信公眾號,這時我們 .NET
社區就開始騷動了起來??,咱們 .NET
能不能也做一個?
既然有需要, 2月6號
那天晚上我決定說干就干,經過兩個晚上的思考與編碼,已經有了初步效果……運行效果如下:

這個效果可以這樣解讀,如果不加以任何控制,疫情會很快蔓延到整個城市,只有 8:1000
床位的城市,將很快失去控制,一年後運行效果如下(非常慘烈):

參數與使用
程式碼中實際有很多參數可以操作:
static double MoveWilling = 0.90f; // 移動意願,0-1 static bool WearMask = false; // 是否戴口罩 static int HospitalBeds = 40; // 床位數 const float InffectRate = 0.8f; // 靠得夠近時,被攜帶者感染的機率 const float SecondsPerDay = 0.3f; // 模擬器的秒數,對應真實一天 const float MovingDistancePerDay = 10.0f; // 每天移動距離 const int InitialInfectorCount = 5; // 最初感染者數 const double DeathRate = 0.021; // 死亡率 // 要靠多近,才會觸發感染驗證 static float SafeDistance() => WearMask ? 1.5f : 3.5f; // 住院治癒時間,最短5天,最長12.75天,平均約7天 static float GenerateCureDays() => random.NextFloat(5, 12.75f); // 潛伏期,1-14天 static float GenerateShadowDays() => random.Next(1, 14); // 發病後,就醫時間,0-3天 static float GenerateToHospitalDays() => random.Next(0, 3);
由於參數太多,很難在運行時全部都做調整,我選取了是否戴口罩、移動意願、醫院床數作為參數,程式碼如下:
protected override void OnKeyPress(KeyPressEventArgs e) { switch (e.KeyChar) { case '1': MoveWilling = 0.10f; break; case '2': MoveWilling = 0.50f; break; case '3': MoveWilling = 0.90f; break; case 'M': WearMask = !WearMask; break; case 'A': HospitalBeds += 40; break; case 'D': HospitalBeds -= 40; break; case 'R': { if (MessageBox.Show("要重來嗎?", "確認", MessageBoxButtons.YesNo) == DialogResult.Yes) { City = City.Create(); } break; } } }
其中,按數字鍵 1
、 2
、 3
可以指定不同的移動意願,其中 1
表示居民最不願意出門, 3
表示最願意,按 M
可以控制居民是否能戴上口罩,按 A
可以添加醫院可接納病人數。
一些演示
超多床位
這裡我將床位調成 240
個(比例為 48
: 1000
):
static int HospitalBeds = 240; // 床位數
也可以運行時增加床位,按鍵盤 A
即可(不用改程式碼),運行效果如下:

可見床位變多後,死亡率會降低,治癒人數大大提高,但仍然無法控制住疫情的發展。
「理想」情況1·沒有潛伏期
可以這樣配置:
// 0潛伏期 static float GenerateShadowDays() => 0;
運行效果如下:

可見就算潛伏期為 0
,由於沒有及時就醫,疫情仍會失去控制。
「理想」情況2·沒有潛伏期、且立刻隔離就醫
像那個 Java
版中,會先介紹一個「理想」情況,即感染即發病,發病即就就醫。此種模型的參數,可以這樣配置:
// 0潛伏期 static float GenerateShadowDays() => 0; // 發病後,立即就醫。 static float GenerateToHospitalDays() => 0;
在這種情況下,運行效果如下:

可見,由最初的 5
人,最終只感染了 6
人,理想情況下局勢很快就被控制。當然這種情況是不存在的。
戴口罩出門
假如居民都戴口罩出門,可以用如下配置(也可以運行時按下 M
鍵):
static bool WearMask = true; // 一定戴口罩
運行效果如下:

堅持了 107
天,疫情發展速度大大降低,但由於醫療資源的匱乏,死亡率仍然居高不下,最終仍會失去控制。
居民呆在家中不出門,且戴口罩
配置如下:
static bool WearMask = true; // 一定戴口罩 static double MoveWilling = 0.10f; // 每天只有10%的人願意出門
運行效果如下:

病毒在最初的 5
人身上,只感染了 2
人。效率可謂驚人。
模擬現實模型
我想貼近現實,模擬一些現實情況,然後看看效果如何,我的腳本如下:
- 前
20
天,不作任何控制 - 第
20
天,全民戴口罩,移動意願降低至50%
,床位增加40
個(模擬進入重大突發公共衛生事件Ⅰ級響應) - 第
25
天,移動願意降低至10%
,床位增加80
個(模擬火神山醫院) - 第
30
天,床位再增加80
個,共240
個(模擬雷神山醫院)
為了儘可能短地截取 gif
,我將時間縮短了,真實時間可能會和這個比例為 1:2
(也就是相當於前 40
天不作任何控制),腳本如下:
void StepDay() { if (day < 20) { WearMask = false; MoveWilling = 0.9; HospitalBeds = 40; } if (day >= 20) { WearMask = true; MoveWilling = 0.5; HospitalBeds = 80; } if (day >= 25) { MoveWilling = 0.1; HospitalBeds = 160; } if (day >= 30) { HospitalBeds = 240; } // 其它程式碼 }
運行效果如下:


可見這真是與病毒的一部史詩級鬥爭。
前 20
天,沒有任何控制,病毒瘋狂肆虐,前期只花了 9
天,就把醫院給擠滿了;
第 20
之後,居民帶上口罩,傳播進度迅速下降,但由於潛伏期較長,發病人數仍然持續上升;
第 30
天之後,由於 5
天前床位(醫護資源)的跟進,發病人數增速降低;
第 35
天后,發病人數開始下降;
第 68
天后,除了醫院中的病人,城市中已經沒有病人;
第 78
天,已經沒有被病毒感染的人了。
總結
所有這些程式碼,我都上傳到了我的部落格數據網站,各們可以下載程式碼,通過 LINQPad6
直接模擬運行,或者拷到 VisualStudio
中亦可。各位也可以或者提出您的想法, Github
鏈接如下:https://github.com/sdcb/blog-data/tree/master/2020/20200207-2019-ncov-simulate
寫完這個東西,給我最大的感受就是震撼,模擬起來就是一些數據而已,但背後是千千萬萬有血有肉的病人和醫務工作者,向那些偉大的「逆行者」們致敬!
從上面的數據也可以看出,任何單項指標做好,都是不能完全阻止疫情的。我們要相互信任,做好自己。我們能做的有:儘可能呆在家,別出門,就真是對國家最好的貢獻;出門一定要戴口罩,這樣可以明顯降低感染率。