1+ import numpy as np
2+ import matplotlib .pyplot as plt
3+
4+
5+ class People (object ):
6+ def __init__ (self , count = 1000 , first_infected_count = 3 ):
7+ self .count = count
8+ self .first_infected_count = first_infected_count
9+ self .init ()
10+
11+ def init (self ):
12+ self ._people = np .random .normal (250 , 100 , (self .count , 2 )) # 产生中值为1,幅度为正负100的,count组样本,每个样本有两个值 作为位置
13+ self .reset ()
14+
15+ def reset (self ):
16+ self ._round = 0 # 表示哪一次循环
17+ self ._status = np .array ([0 ] * self .count )
18+ self ._timer = np .array ([0 ] * self .count )
19+ self .random_people_state (self .first_infected_count , 1 )
20+
21+ def random_people_state (self , num , state = 1 ):
22+ """随机挑选人设置状态
23+ """
24+ assert self .count > num
25+ # TODO:极端情况下会出现无限循环
26+ n = 0
27+ while n < num :
28+ i = np .random .randint (0 , self .count )
29+ if self ._status [i ] == state :
30+ continue
31+ else :
32+ self .set_state (i , state )
33+ n += 1
34+
35+ def set_state (self , i , state ):
36+ self ._status [i ] = state
37+ # 记录状态改变的时间
38+ self ._timer [i ] = self ._round # 哪次循环中状态发生在哪次循环中
39+
40+ def move (self , width = 1 , x = .0 ):
41+ movement = np .random .normal (0 , width , (self .count , 2 ))
42+
43+ normal = np .random .normal (0 , 1 , self .count )
44+ switch = np .where (normal < x , 1 , 0 )
45+ movement [switch == 0 ] = 0 # 随机产生不移动的情况
46+ self ._people = self ._people + movement # 位置发生变换
47+
48+ def change_state (self ): # 设置成为确证
49+ dt = self ._round - self ._timer
50+ # 必须先更新时钟再更新状态
51+ d = np .random .randint (3 , 7 )
52+ # print("change_state:", (self._status == 1) & ((dt == d) | (dt > 14)))
53+ self ._timer [(self ._status == 1 ) & ((dt == d ) | (dt > 14 ))] = self ._round
54+ self ._status [(self ._status == 1 ) & ((dt == d ) | (dt > 14 ))] += 1
55+
56+ def affect (self , safe_distance = 5 ):
57+ """感染最接近的健康人"""
58+ # np.vstack((self._people[self._status == 1],self._people[self._status == 2]))
59+ for inf in self ._people [(self ._status == 1 ) | (self ._status == 2 )]: # self.infected:
60+ dm = (self ._people - inf ) ** 2
61+ d = dm .sum (axis = 1 ) ** 0.5 # 计算一个欧氏距离 (x1,y1) (x2,y2) ==> ((x1-x2)^2 + (y1-y2)^2)^(1/2)
62+ sorted_index = d .argsort ()
63+ for i in sorted_index :
64+ if d [i ] >= safe_distance :
65+ break # 超出范围,不用管了
66+ if self ._status [i ] > 0 : # 已经感染的排除掉
67+ continue
68+ self ._status [i ] = 1
69+ # 记录状态改变的时间
70+ self ._timer [i ] = self ._round
71+ break # 只传 1 个
72+
73+ def update (self ):
74+ """每一次迭代更新"""
75+ self .change_state ()
76+ self .affect ()
77+ self .move (3 , 1.99 )
78+ self ._round += 1
0 commit comments