小编说:本文由博文视点一位身在武汉的作者龙中华老师,根据Ele实验室发布的“疫情传播仿真程序”视频以及其开源代码整理编辑而成,首发于个人博客:
https://blog.csdn.net/u013840066/article/details/104212919
前几天,“Ele实验室” Bruce Young同学在家制作了一个有关病毒传播的仿真程序,为我们带来了极大的视角上的震撼,对于人们认识病毒传播有很大的价值,于是这里把源代码分享出来(版权归Bruce Young同学),因为该程序实际上没有实际的商用价值(有很大的传播教育价值),所以我们只做一些简单的讲解。
视频中程序代码GitHub开源链接:
https://github.com/KikiLetGo/VirusBroadcast/tree/master/src
下面我们进入正题,运行效果图如下图所示:
该程序主要使用Swing( 一个为Java设计的GUI工具包)来绘制图形用户界面(GUI)。实现的步骤如下:
创建Point 类
该类用于定义绘制图形界面上的点,代码如下:
代码语言:javascript复制public class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
创建常量类
该类用于调整参数来展现不同的效果,见下方代码:
代码语言:javascript复制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;
}
创建城市类
该类用于定义一个城市。
代码语言:javascript复制public class City {
private int centerX;
private int centerY;
public City(int centerX, int centerY) {
this.centerX = centerX;
this.centerY = centerY;
}
public int getCenterX() {
return centerX;
}
public void setCenterX(int centerX) {
this.centerX = centerX;
}
public int getCenterY() {
return centerY;
}
public void setCenterY(int centerY) {
this.centerY = centerY;
}
}
创建医院类
该类用于创建一个演示的医院类。
代码语言:javascript复制public class Hospital {
private int x=800;
private int y=110;
private int width;
private int height=606;
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
private static Hospital hospital = new Hospital();
public static Hospital getInstance(){
return hospital;
}
private Point point = new Point(800,100);
private List<Bed> beds = new ArrayList<>();
private Hospital() {
if(Constants.BED_COUNT==0){
width=0;
height=0;
}
int column = Constants.BED_COUNT/100;
width = column*6;
for(int i=0;i<column;i ){
for(int j=10;j<=610;j =6){
Bed bed = new Bed(point.getX() i*6,point.getY() j);
beds.add(bed);
}
}
}
public Bed pickBed(){
for(Bed bed:beds){
if(bed.isEmpty()){
return bed;
}
}
return null;
}
}
创建医院的床位类
该类创建一个用于演示的医院的病床类。
代码语言:javascript复制public class Bed extends Point{
public Bed(int x, int y) {
super(x, y);
}
private boolean isEmpty=true;
public boolean isEmpty() {
return isEmpty;
}
public void setEmpty(boolean empty) {
isEmpty = empty;
}
}
创建PersonPool类
该类创建PersonPool,用于管理城市大小和人数。
代码语言:javascript复制public class PersonPool {
private static PersonPool personPool = new PersonPool();
public static PersonPool getInstance(){
return personPool;
}
List<Person> personList = new ArrayList<Person>();
public List<Person> getPersonList() {
return personList;
}
private PersonPool() {
City city = new City(400,400);
for (int i = 0; i < 5000; i ) {
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);
}
}
}
创建Person类
代码语言:javascript复制public class Person {
private City city;
private int x;
private int y;
private MoveTarget moveTarget;
int sig=1;
double targetXU;
double targetYU;
double targetSig=50;
public interface State{
int NORMAL = 0;
int SUSPECTED = NORMAL 1;
int SHADOW = SUSPECTED 1;
int CONFIRMED = SHADOW 1;
int FREEZE = CONFIRMED 1;
int CURED = FREEZE 1;
}
public Person(City city, int x, int y) {
this.city = city;
this.x = x;
this.y = y;
targetXU = 100*new Random().nextGaussian() x;
targetYU = 100*new Random().nextGaussian() y;
}
public boolean wantMove(){
double value = sig*new Random().nextGaussian() Constants.u;
return value>0;
}
private int state=State.NORMAL;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
int infectedTime=0;
int confirmedTime=0;
public boolean isInfected(){
return state>=State.SHADOW;
}
public void beInfected(){
state = State.SHADOW;
infectedTime=MyPanel.worldTime;
}
public double distance(Person person){
return Math.sqrt(Math.pow(x-person.getX(),2) Math.pow(y-person.getY(),2));
}
private void freezy(){
state = State.FREEZE;
}
private void moveTo(int x,int y){
this.x =x;
this.y =y;
}
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);
}
int dX = moveTarget.getX()-x;
int dY = moveTarget.getY()-y;
double length=Math.sqrt(Math.pow(dX,2) Math.pow(dY,2));
if(length<1){
moveTarget.setArrived(true);
return;
}
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&&udY!=0){
if(dY>0){
udY=1;
}else{
udY=-1;
}
}
if(x>700){
moveTarget=null;
if(udX>0){
udX=-udX;
}
}
moveTo(udX,udY);
// if(wantMove()){
// }
}
private float SAFE_DIST = 2f;
public void update(){
//@TODO找时间改为状态机
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;
}
float random = new Random().nextFloat();
if(random<Constants.BROAD_RATE&&distance(person)<SAFE_DIST){
this.beInfected();
}
}
}
}
创建MyPanel类
代码语言:javascript复制public class MyPanel extends JPanel implements Runnable {
private int pIndex=0;
public MyPanel() {
this.setBackground(new Color(0x444444));
}
@Override
public void paint(Graphics arg0) {
super.paint(arg0);
//draw border
arg0.setColor(new Color(0x00ff00));
arg0.drawRect(Hospital.getInstance().getX(),Hospital.getInstance().getY(),
Hospital.getInstance().getWidth(),Hospital.getInstance().getHeight());
List<Person> people = PersonPool.getInstance().getPersonList();
if(people==null){
return;
}
people.get(pIndex).update();
for(Person person:people){
switch (person.getState()){
case Person.State.NORMAL:{
arg0.setColor(new Color(0xdddddd));
}break;
case Person.State.SHADOW:{
arg0.setColor(new Color(0xffee00));
}break;
case Person.State.CONFIRMED:
case Person.State.FREEZE:{
arg0.setColor(new Color(0xff0000));
}break;
}
person.update();
arg0.fillOval(person.getX(), person.getY(), 3, 3);
}
pIndex ;
if(pIndex>=people.size()){
pIndex=0;
}
}
public static int worldTime=0;
@Override
public void run() {
while (true) {
this.repaint();
try {
Thread.sleep(100);
worldTime ;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
创建MoveTarget 类
创建MoveTarget 类用于模拟人群流动。
代码语言:javascript复制public class MoveTarget {
private int x;
private int y;
private boolean arrived=false;
public MoveTarget(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public boolean isArrived() {
return arrived;
}
public void setArrived(boolean arrived) {
this.arrived = arrived;
}
}
修改入口类
修改入口类,绘制图形
代码语言:javascript复制import javax.swing.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Main {
public static void main(String[] args) {
MyPanel p = new MyPanel();
Thread panelThread = new Thread(p);
JFrame frame = new JFrame();
frame.add(p);
frame.setSize(1000, 800);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panelThread.start();
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();
}
}
}
到此完成,运行程序即可。
图书推荐
《Spring Boot实战派》
龙中华 著
- 让开发像搭积木一样简单,在实战情景中学习,学完即知怎么实战
版本点新 针对 Spring Boot 2.0 及以上版本
体例科学 用“知识点 实例”形式编写
实例丰富 58个基础实例 2个综合项目
对比选优 对比讲解多种同类技术,便于技术选型,如 Spring Security 和 Shiro、Elasticsearch 和 Solr、JPA 和 Mybatis
技术点新 讲解了时下流行的接口架构风格 RESTful 、用来实现高并发的 Redis 、以及用来实现系统间通信的中间件 RabbitMQ
▊ 关于作者
龙中华
12年来一直在某一线互联网公司担任资深系统分析师。
目前带领3个研发团队,承担系统的分析、设计、实施、演进,以及技术团队管理和培训等职责。有独到的团队建设和管理经验,对互联网多种技术特点和发展趋势有较深入的研究,对多种技术(如 Spring Boot 、Spring Cloud 和 Service Mesh )有深入的研究和实战经验。