Java丨Ele实验室“疫情传播仿真程序”的代码实现

陈晓猛

2020-02-08

小编说:本文由博文视点一位身在武汉的作者龙中华老师,根据Ele实验室发布的“疫情传播仿真程序”视频以及其开源代码整理编辑而成首发于个人博客:
https://blog.csdn.net/u013840066/article/details/104212919

前几天,“Ele实验室” Bruce Young同学在家制作了一个有关病毒传播的仿真程序(https://www.bilibili.com/video/av86478875),为我们带来了极大的视角上的震撼,对于人们认识病毒传播有很大的价值,于是这里把源代码分享出来(版权归Bruce Young同学),因为该程序实际上没有实际的商用价值(有很大的传播教育价值),所以我们只做一些简单的讲解。

视频中程序代码GitHub开源链接:

https://github.com/KikiLetGo/VirusBroadcast/tree/master/src

下面我们进入正题,运行效果图如下图所示:

该程序主要使用Swing( 一个为Java设计的GUI工具包)来绘制图形用户界面(GUI)。实现的步骤如下:

创建Point 类

该类用于定义绘制图形界面上的点,代码如下:

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;
    }
  }

创建常量类

该类用于调整参数来展现不同的效果,见下方代码:

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

创建城市类

该类用于定义一个城市。

  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;
    }
  }

创建医院类

该类用于创建一个演示的医院类。

  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;
    }
  }

创建医院的床位类

该类创建一个用于演示的医院的病床类。

  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,用于管理城市大小和人数。

  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类

  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;121            }
        }

        if(x>700){
            moveTarget=null;
            if(udX>0){
                udX=-udX;
            }
        }
        moveTo(udX,udY);

//        if(wantMove()){
//        }


    }

    private float SAFE_DIST = 2f;

    public void update(){
        //@TODO找时间改为状态机142        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;166        }
       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类

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) {5657            this.repaint();5859            try {
                Thread.sleep(100);
                worldTime++;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }


}

创建MoveTarget 类

创建MoveTarget 类用于模拟人群流动。

 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;17    }
    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;
    }
 }

修改入口类

修改入口类,绘制图形

  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 )有深入的研究和实战经验。

(扫码了解本书详情)

扫码了解本书详情

读者评论

相关专题

相关博文

  • Get不到AI的点?一定要看《程序员的AI书:从代码开始》!

    Get不到AI的点?一定要看《程序员的AI书:从代码开始》!

    陈晓猛 2020-03-30

    机器学习火起来也有几年了, 当老姑大伯们渐渐把AI和程序员画上等号时,我大腿一拍大事不妙!生怕疫情后的家庭聚会上,让我表演才艺:做个什么狗陪他们下棋、做个什么精灵跟他们唠嗑…… 程序员群体很广的!我们也不是什么都懂,更何况我还...

    陈晓猛 2020-03-30
    650 0 0 1
  • 聊聊高并发之隔离术

    聊聊高并发之隔离术

    张开涛 2017-04-21

    隔离是指将系统或资源分割开,系统隔离是为了在系统发生故障时能限定传播范围和影响范围,即发生故障后不会出现滚雪球效应,从而保证只有出问题的服务不可用,其他服务还是可用的;而资源隔离有脏数据隔离、通过隔离后减少资源竞争提升性能等。我遇到的比...

    张开涛 2017-04-21
    1796 0 0 0
  • 了解智能一体化

    了解智能一体化

    陈绍英 2017-04-21

    了解智能一体化测试平台 智能一体化测试平台是为支持智能一体化测试理论而开发的平台,这个平台主要面向后台系统的服务/接口测试。借助这个平台,开发测试人员进行服务/接口测试时可以将工作重心集中在测试案例设计与管理上,测试执行与分析主要交...

    陈绍英 2017-04-21
    1018 0 0 0