病毒扩散仿真java程序,仿真模拟新冠肺炎病毒扩散

2024-05-27 06:58

本文主要是介绍病毒扩散仿真java程序,仿真模拟新冠肺炎病毒扩散,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

GitHub 地址如下:https://github.com/KikiLetGo/VirusBroadcast

源码结构

源码结构比较简单,我们来一起看一下:

模型讲解

我对仿真模型做了一个抽象和概括,我们一起对照着源码分析模型的整个模拟过程和思路。

模型前提设置

首先,假设 C(400,400) 是城市的中心,整个城市是以 C 为中心的圆,L=100 是圆的半径。

假设 P(x,y) 就表示城市中的人,人受疫情影响有不同的状态 S:

  • S.NORMAL=0:正常。
  • S.SUSPECTED=1:疑似。
  • S.SHADOW=2:病毒携带潜伏者。
  • S.CONFIRMED=3:确诊。
  • S.FREEZE=4:隔离。
  • S.CURED=5:治愈。

对应于感染者,和确诊者分别设置 infectedTime(被感染的时刻)和 confirmedTime(确诊的时刻)。

其次,假设医院是高为 H,宽为 W 的长方形区域,其中矩形左下角坐标为 H(800,110)。

为了表示医院容量的大小,我们把 H=606 设为常量,则 W 越大表示医院的可容纳量越大(也即床位越多);然后,假设 B(x,y) 就表示位于医院内的床位。

最后我们要设置一些启动参数:

  • int ORIGINAL_COUNT=50:初始感染数量。
  • float BROAD_RATE=0.8f:传播率。
  • float SHADOW_TIME=140:潜伏时间。
  • int HOSPITAL_RECEIVE_TIME=10:医院收治响应时间。
  • int BED_COUNT=1000:医院床位。
  • float u=0.99f:流动意向平均值。

模型启动初始化

模型启动时,我们在以 C 为中心 L 为半径的圆内随机产生 5000 个 P:

/** * 以(400,400)为城市中心,在方圆100单位长度以内, * 伪随机(近似正态分布)出5000人; * 如果person的x轴坐标超过了700,则就按700算(为了限制到一定范围内) */ private PersonPool() { City city = new City(400,400); for (int i = 0; i < 5000; i++) { /** * random.nextGaussian() * 返回均值0.0和标准差1的伪随机(近似)正态分布的double。 */ Random random = new Random(); int x = (int) (100 * random.nextGaussian() + city.getCenterX()); int y = (int) (100 * random.nextGaussian() + city.getCenterY()); if(x>700){ x=700; } Person person = new Person(city,x,y); personList.add(person); } } 

并根据 ORIGINAL_COUNT=50:初始感染数量,初始化 50 个感染者(状态为 S.SHADOW 的 P):

List<Person> people = PersonPool.getInstance().getPersonList(); for(int i=0;i<Constants.ORIGINAL_COUNT;i++){ //生成人口规模范围内的随机整数 int index = new Random().nextInt(people.size()-1); Person person = people.get(index); //避免随机值碰撞 while (person.isInfected()){ index = new Random().nextInt(people.size()-1); person = people.get(index); } //生成感染者 person.beInfected(); } 

模型运行

启动之后模型就开始模拟人员流动,模拟病毒随人群如何传播,以及医院如何收治,我这里着重讲解一下。

①模拟人员流动

首先要知道,P 是否流动与 P 的状态 S 和流动意愿值有关系,如果 S=S.FREEZE(也即被医院隔离)则无法流动,如果 P 不想动则也不会流动。其中这里流动意愿值如何计算的呢?

个人流动意愿值=流动意向平均值+随机流动意向:

public boolean wantMove(){ //流动意向平均值+随机流动意向 double value = sig*new Random().nextGaussian()+Constants.u; return value>0; } 

P(x1,y1) 初次流动时会随机产生一个 T(x2,y2) 目标地,且 T 是限制在以 P 为圆心的一定范围内的。

那么 P 是如何向 T 流动的呢?这里不是简单的直接 moveTo(T),为了更真实模拟实际情况,P 其实是逐渐靠近 T 的。

 

假设 D 是 P 到 T 之间的距离,则 D = sqrt(pow(x1-x2,2)+pow(y1-y2,2)) :

  • 若 D<1,则认为 P 已经到达 T。
  • 若 D>1,则下一次 P 到达的坐标是 [(x2-x1)/|x2-x1|,(y2-y1)/|y2-y1|],其实就是超过了 -1,还没到 +1。

P 到达目的地后就不动了吗?不是的,P 到达目的地后会在随机产生下一个目的地,然后以同样的算法趋近目的地。

private void action(){ //已隔离,无法行动 if(state==State.FREEZE){ return; } //不想动,也无法行动 if(!wantMove()){ return; } //如果还没有行动过,或者目标地已经到达,则重新随机产生下一个目标地 if(moveTarget==null||moveTarget.isArrived()){ double targetX = targetSig*new Random().nextGaussian()+targetXU; double targetY = targetSig*new Random().nextGaussian()+targetYU; moveTarget = new MoveTarget((int)targetX,(int)targetY); } /** * dX : 目标地与当前位置的相对x轴坐标差 * dY : 目标地与当前位置的相对y轴坐标差 * length : 目标地与当前位置的距离 */ int dX = moveTarget.getX()-x; int dY = moveTarget.getY()-y; double length=Math.sqrt(Math.pow(dX,2)+Math.pow(dY,2)); //如果目标地与当前位置误差在1步长内,则视为已经到达目的地 if(length<1){ moveTarget.setArrived(true); return; } //否则,缩小每次移动的步长,控制在(1,根号2)以内 int udX = (int) (dX/length); if(udX==0&&dX!=0){ if(dX>0){ udX=1; }else{ udX=-1; } } int udY = (int) (dY/length); if(udY==0&&dY!=0){ if(dY>0){ udY=1; }else{ udY=-1; } } //如果当前位置已经超出边界,则重新规划目的地,并往回走udx个步长 if(x>700){ moveTarget=null; if(udX>0){ udX=-udX; } } moveTo(udX,udY); } 

②模拟病毒传播与医院收治

因为有没有感染病毒,有没有隔离病毒,其实都是和人有关系,所以模拟病毒传播其实就是模拟 P 的状态 S 的变迁。

这里有一个前提说明:设置 worldTime 表示当前时刻,初始化为 0,JPanel 面板每刷新一次,worldTime+1。

  • 若 S=S.FREEZE,则 P 已经被医院收治,已被隔离。状态不更新。
  • 若 S=S.CONFIRMED,且 worldTime-confirmedTime>=Constants.HOSPITAL_RECEIVE_TIME,也即 P 已确诊且距确诊时间已经超过医院反应时间,则说明 P 应该被医院收治。
  • 但是如果医院有床位,则将 P(x1,y1) 移动到 B(x2,y2),即表示已收容;如果医院没有床位了,则 P(x1,y1) 无法收容,依然参与人员流动过程。
  • 若 S=S.SHADOW,且 worldTime-infectedTime>Constants.SHADOW_TIME,也即 P 是已被感染者,且感染期限超出潜伏期,则此时应转为 CONFIRMED(确诊)状态。

状态迁移搞清楚了,那还有一个问题,正常人是如何被感染的?这与两个参数有关:

  • BROAD_RATE,这个是我们上面提到过的传播率参数,表示人是否被感染有一定概率。
  • SAFE_DIST,表示正常人和疑似者/感染者/确诊者等之间的安全距离。

当概率随机值超过 BROAD_RATE,且正常人和疑似者/感染者/确诊者等之间的距离小于 SAFE_DIST 时,正常人会被成为感染者,状态 S=S.SHADOW(潜伏者):

public void update(){ //已隔离,状态不更新 if(state>=State.FREEZE){ return; } //若已确诊时长超过医院反应时间,则表示此确诊者已被隔离到医院 if(state==State.CONFIRMED&&MyPanel.worldTime-confirmedTime>=Constants.HOSPITAL_RECEIVE_TIME){ Bed bed = Hospital.getInstance().pickBed(); if(bed==null){ System.out.println("隔离区没有空床位"); }else{ //被隔离起来了 state=State.FREEZE; x=bed.getX(); y=bed.getY(); bed.setEmpty(false); } } //若已感染时长超过潜伏期,则潜伏者就会确诊,确诊时间就是当前时间 if(MyPanel.worldTime-infectedTime>Constants.SHADOW_TIME&&state==State.SHADOW){ state=State.CONFIRMED; confirmedTime = MyPanel.worldTime; } action(); List<Person> people = PersonPool.getInstance().personList; if(state>=State.SHADOW){ return; } for(Person person:people){ if(person.getState()== State.NORMAL){ continue; } /** * Random().nextFloat() * 用于获取下一个从这个伪随机数生成器的序列中均匀分布的0.0和1.0之间的float值 */ float random = new Random().nextFloat(); //随机float值小于传播率,且与感染者安全距离小于SAFE_DIST时,此人就会别感染 if(random<Constants.BROAD_RATE&&distance(person)<SAFE_DIST){ this.beInfected(); } } } 

调节参数来模拟效果

我们上面提到了启动仿真所需的那些参数:

public class Constants { public static int ORIGINAL_COUNT=50;//初始感染数量 public static float BROAD_RATE = 0.8f;//传播率 public static float SHADOW_TIME = 140;//潜伏时间 public static int HOSPITAL_RECEIVE_TIME=10;//医院收治响应时间 public static int BED_COUNT=1000;//医院床位 public static float u=0.99f;//流动意向平均值 
} 

根据模拟效果可以明显看出来,流动意愿平均值是一个很重要的参数,即使是传播率较大,医院资源紧缺,潜伏期较长的情况下,只要大家都不出门,有效控制人群流动,那么疫情很快就可以被消灭。

所以“防疫的中坚力量其实是广大的人民群众,忍一时风平浪静,别在往出去跑给国家添麻烦了!”

模型优化

其实这个模型并不复杂,简单总结一下:

  • 这里模拟的是一个城市,且城市模型是理想化的。
  • 人群分布是伪随机正态分布的。
  • 人的流动模型很简单,就是一个点向另一个点以小步长趋近。
  • 病毒传播模型就是根据一定概率加上安全距离的限定来模拟人传人。
  • 医院收治模型就是根据感染时长和确诊时长来模拟收治。

针对这几个点,想到的优化思路:

  • 多个城市中心(这也是程序作者的意见之一)。
  • 人群分布可以调参,可以根据实际情况来确定分布密度。
  • 在加上收治病人治愈出院的情况,更加符合实际。
  • 病毒传染更加科学准确的模型(因为一个人染上病是多方面因素的综合叠加),作者尝试使用DNN对病毒进行建模(包括病毒变异)。

这篇关于病毒扩散仿真java程序,仿真模拟新冠肺炎病毒扩散的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1006764

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象