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