1. 程式人生 > 程式設計 >Python實現病毒模擬器的方法示例(附demo)

Python實現病毒模擬器的方法示例(附demo)

最近新冠在神州大陸橫行,全國上下一心抗擊疫情。作為一枚程式設計師,我也希望可以為抗擊疫情做出自己的貢獻,鍾院士一直勸說大家不要出門,減少人口間的流動。對此,我特意做了一個病毒模擬器,探詢冠狀病毒傳播。

1. 模擬效果

模擬開始,一開始只有5個發病者,傳播率為0.8,潛伏期為14天

Python實現病毒模擬器的方法示例(附demo)

由於人口的流動,以及醫院床位的隔離,一開始病毒擴撒不是很速度

Python實現病毒模擬器的方法示例(附demo)

隨著醫院床位滿了,隔離失敗,加上人口的流動,病患數開始幾何式的增加

Python實現病毒模擬器的方法示例(附demo)

2. 什麼是模擬器

模擬器(emulator)以某一系統復現另一系統的功能。與計算機模擬系統(Computer Simulation)的區別在於,模擬器致力於模仿系統的外在表現、行為,而不是模擬系統的抽象模型。

3. python如何實現模擬器

現在來談談模擬器實現的原理。模擬器使用Python和PyQt5實現。PyQt5是封裝了Qt library的跨平臺GUI開發庫,基於Python語言。

這裡主要涉及到模擬器效果繪製,以及如何模擬多個引數。先來說一下繪製市民的狀態。繪製的工作通過drawing.py檔案的Drawing類來完成。該類是QWidget的子類,這也就意味著Drawing類本身是PyQt5的一個元件。與按鈕、標籤類似。只是並不需要往Drawing上放置任何子元件。只要在Drawing上繪製各種圖形即可。

在PyQt5中,任何一個QWidget的子類,都可以實現一個paintEvent方法,當元件每次重新整理時,就會呼叫paintEvent方法重新繪製元件的內容。Drawing類中paintEvent方法的程式碼如下:

 # 每次Drawing重新整理,都會呼叫該方法
  def paintEvent(self,event):
    qp = QPainter()
    qp.begin(self)
    self.drawing(qp) # 繪製城市的各種狀態的市民
    qp.end()

在繪製圖像前,需要建立QPainter物件,然後呼叫QPainter物件的begin方法,結束繪製後,需要呼叫QPainter物件的end方法。上面程式碼中的drawing方法用於完成具體的繪製工作。

模擬器設定一個人員池,來進行人員的流動性

class Persons(metaclass=Singleton):
  def __init__(self):
    self.persons = []          # 儲存所有的人員
    self.latency_persons = []      # 儲存處於潛伏期的人員
 
    city = City(Params.city_center_x,Params.city_center_y)
 
    for value in range(0,Params.city_person_count):
      x = Params.person_position_scale * next_gaussian() + city.center_x
      y = Params.person_position_scale * next_gaussian() + city.center_y
      if x > Params.city_width:
        x = Params.city_width
      if y > Params.city_height:
        y = Params.city_height
      self.persons.append(Person(city,x,y))
  # 獲取特定人群的數量
  def get_person_size(self,state):
    if state == -1:
      return len(self.persons)
    count = 0
    for person in self.persons:
      if person.state == state:
        count += 1
    return count

模擬程式碼

from PyQt5.QtWidgets import *
from socket import *
 
class Transmission:
  def __init__(self,ui):
    self.ui = ui
    self.host = 'localhost'
    self.port = 5678
    self.addr = (self.host,self.port)
 
  def send_command(self,command,value=None):
    tcp_client_socket = socket(AF_INET,SOCK_STREAM)
    tcp_client_socket.connect(self.addr)
 
    if value == None:
      value = 0
 
    data = command + ':' + str(value)
    tcp_client_socket.send(('%s\r\n' % data).encode(encoding='utf-8'))
    data = tcp_client_socket.recv(1024)
    result = data.decode('utf-8').strip()
    tcp_client_socket.close()
    return result
  def setup(self):
    self.ui.horizontalSliderBedCount.valueChanged.connect(self.bed_count_value_change)
    self.ui.pushButtonUpdateBedCount.clicked.connect(self.update_bed_count)
 
    self.ui.horizontalSliderFlowIntention.valueChanged.connect(self.flow_intention_value_change)
    self.ui.pushButtonFlowIntention.clicked.connect(self.update_flow_intention)
 
    self.ui.horizontalSliderBroadRate.valueChanged.connect(self.broad_rate_value_change)
    self.ui.pushButtonBroadRate.clicked.connect(self.update_broad_rate)
 
    self.ui.horizontalSliderLatency.valueChanged.connect(self.latency_value_change)
    self.ui.pushButtonLatency.clicked.connect(self.update_latency)
 
    self.ui.pushButtonClose.clicked.connect(self.close_virus_simulation)
 
  def bed_count_value_change(self):
    self.ui.labelBedIncrement.setText(
      f'<html><head/><body><p><span style=\" font-size:24pt; color:#0000ff;\">{self.ui.horizontalSliderBedCount.value()}</span></p></body></html>')
 
  def update_bed_count(self):
    print(self.ui.horizontalSliderBedCount.value())
    result = self.send_command('add_bed_count',self.ui.horizontalSliderBedCount.value())
    if result == 'ok':
      QMessageBox.information(self.ui.centralwidget,'訊息',f'成功添加了{self.ui.horizontalSliderBedCount.value()}張床位',QMessageBox.Ok)
 
  def flow_intention_value_change(self):
    self.ui.labelFlowIntention.setText(
      f'<html><head/><body><p><span style=\" font-size:24pt; color:#fc02ff;\">{self.ui.horizontalSliderFlowIntention.value() / 100}</span></p></body></html>')
 
  def update_flow_intention(self):
    result = self.send_command('set_flow_intention',self.ui.horizontalSliderFlowIntention.value())
    if result == 'ok':
      QMessageBox.information(self.ui.centralwidget,f'成功設定流動意向為{self.ui.horizontalSliderFlowIntention.value() / 100}',QMessageBox.Ok)
 
  def broad_rate_value_change(self):
    self.ui.labelBroadRate.setText(
      f'<html><head/><body><p><span style=\" font-size:24pt; color:#fc0107;\">{self.ui.horizontalSliderBroadRate.value() / 100}</span></p></body></html>')
 
  def update_broad_rate(self):
    result = self.send_command('set_broad_rate',self.ui.horizontalSliderBroadRate.value())
    if result == 'ok':
      QMessageBox.information(self.ui.centralwidget,f'成功設定傳播率為{self.ui.horizontalSliderBroadRate.value() / 100}',QMessageBox.Ok)
 
  def latency_value_change(self):
    self.ui.labelLatency.setText(
      f'<html><head/><body><p><span style=\" font-size:24pt; color:#ffff0a;\">{self.ui.horizontalSliderLatency.value()}</span></p></body></html>')
 
  def update_latency(self):
    result = self.send_command('set_latency',self.ui.horizontalSliderLatency.value())
    if result == 'ok':
      QMessageBox.information(self.ui.centralwidget,f'成功設定傳播率為{self.ui.horizontalSliderLatency.value()}',QMessageBox.Ok)
 
  def close_virus_simulation(self):
    reply = QMessageBox.information(self.ui.centralwidget,"請問","是否真的要關閉病毒擴散模擬器?",QMessageBox.Yes | QMessageBox.No,QMessageBox.No)
    if reply == QMessageBox.Yes:
      result = self.send_command('close')
      if result == 'ok':
        QMessageBox.information(self.ui.centralwidget,'已經成功關閉病毒擴散模擬器',QMessageBox.Ok)

設定變數

class Params:
  success = False
 
  # 初始感染人數
  original_infected_count = 50
 
  # 病毒傳播率
  broad_rate = 0.8
 
  # 病毒潛伏期,14天
  virus_latency = 140
 
  # 醫院收治響應時間
  hospital_receive_time = 10
 
  # 醫院床位
  hospital_bed_count = 100
 
  # 安全距離
  safe_distance = 2
 
  # 平均流動意向[-3,3]  值越大,流動意向越強
  average_flow_intention = 3
 
  # 城市總人口數量
  city_person_count = 5000
 
  # 病死率
  fatality_rate = 0.02
 
  # 死亡時間
  dead_time = 30
  # 死亡時間方差
  dead_variance = 30
 
  # 城市寬度
  city_width = 1100
 
  # 城市高度
  city_height = 800
 
  # 醫院寬度(需要計算獲得)
  hospial_width = 0
 
  # 城市中心x座標
  city_center_x = 550
 
  # 城市中心y座標
  city_center_y = 400
 
  # 用於計算城市中每個人隨機位置的scale(用於正態分佈)
  person_position_scale = 200
 
  current_time = 1 # 當前時間

我們可以根據這些修改模擬頁面

Python實現病毒模擬器的方法示例(附demo)

完整程式碼

以上就是Python實現病毒模擬器的方法示例的詳細內容,更多關於請關注我們其它相關文章!