程序员用代码告诉你,千万别出门!

前些天,一个视频火了起来。

事情是这样的,B 站 UP 主 @ele实验室,用了一夜的时间,写了一个简单的疫情传播仿真程序,告诉大家在家待着的重要性,视频如下:

   

 点击查看视频

来自B站up主“Ele实验室”,版权属于原作者

现在,作者已经将代码开源到 GitHub 上,场主特此 download 下来分享给大家。
GitHub 地址如下:
https://github.com/KikiLetGo/VirusBroadcast 

此后,有人解析了VirusBroadcast的程序代码,下面我们就来看看它是如何一步步实现的吧!

这个程序主要利用“高斯分布”算法,简单的模拟病毒传播的场景。它由以下程序片段构成

1、病毒对象

2、床位【隔离对象】

3、主要参数【初始状态】,假设没有隔离,潜伏期为一天

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

4、医院对象,随机分布,产生床位

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


5、流动目的性

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

6、人员【流动及感染】

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




    }


    private float SAFE_DIST = 2f;


    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;
           }
           float random = new Random().nextFloat();
           if(random<Constants.BROAD_RATE&&distance(person)<SAFE_DIST){
               this.beInfected();
           }
       }
    }
}

7、汇总人数

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

8、分布点

9、显示面板,设置色块【红色:感染   黄色:潜伏  绿色:安全】

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

10、主程序【运行】

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();
        }
    }
}
(作者也强调这个基于Java的模拟仿真程序由于启动仓促,仍有不足,仅供广大网友参考学习)

此外,作者提到,人的流动意向、初始的传播率等等目前都是正态分布的简单模型,较为固定,所以结果也较为理想。
 
假设一个城市初始有50个感染者,而医院还没有设立隔离区,人们的流动意向也是比较高的状态下,传播的状态时这样的。

病毒将快速传播。

而如果把人员流动的意向降低,传播效率就会急剧减缓,但病毒还是会慢慢的扩散。

这时,就需要医院的隔离措施上场了。

如果我们把人们的流动意向再次调高,那么不难发现,想要控制住就只能增加医院的隔离床位了。

当然,作者还提到这里还有一个参数,叫做医院收治响应的时间,就是一个患者从确诊到被安置隔离的时间,目前响应的速度都在变快,看起来也不用太担心。

病毒有14天的潜伏期,并不是所有人都会在同一时间爆发病情,因此,过了潜伏期后,就有了前几天数据飙升的现象。

但如果这时我们再次控制人口流动意向,病情虽然还在缓慢增长,但最终还是会得到控制。所以,“人群流动意向”这个参数起到了至关重要的作用,而这个参数,与我们息息相关。
 
所以,别慌,再等一等。

本文来源于网络,版权归原作者所有,如有侵权,请联系删除。

更多干货:

             欢迎加入高薪求职大家庭

听说进入IT达人圈子的童鞋,运气都比以前好!因为你获得比别人更多的机会,更大的视野!

目前IT达人圈有微信群,专门做行业交流,里面有IT公司招聘内推和猎头职位,沟通氛围热烈,是个大群。

群目的:IT公司招聘内推、IT人员求职,行业交流聚会等等,目前内推的公司包括互联网、证券公司、IBM、H3C、各大咨询机构、各大IT服务管理公司,另外还有猎头职位。

入群要求::IT从业人员,重点是IT管理人员、IT运维人员,IT安全人员,研发人员,仅限于技术人员交流,业务人员勿扰。

加群方法:加微信:gogoat01,备注:求职群,公司+职位+姓名+地区,真实的社群才有价值。另外有一些职位,会根据自我介绍推荐

温馨提示:管理员平时比较忙,所以会响应慢点,会集中一段时间处理,请见谅

   

      

            

 编辑:Gogoat

18年IT生涯,非著名IT策划人

擅长连接,最受信赖的IT社区管理员 

职位、干货投稿;加入圈子请扫一扫

    

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值