有幸参加了2019年美国大学生数学建模比赛。比赛过程中担任的角色中有编程任务,现将当时的代码和大家分享。比赛结束后抽空将代码完善了一下,仍然有诸多不足之处。
我们团队怎么也找不到卢浮宫的建筑平面图,有找到的吗????不能精确定位模拟,所以就基于疏散问题建立了自己的平面图。
1、假设(部分)
(1)场馆内无障碍物(这样简化了很多)
(2)场馆内人员单位时间内最多能移动一个元胞距离(很关键,不然非常)
2、馆内人员移动分析
即单位时间内可以移动八个方向,每次一个元胞距离
3、安全出口数量以及安全出口的考虑
如果考虑多个安全出口,仅仅比赛那点时间,我这点水平编程是不够的,所以指定只有一个安全出口;在考虑安全出口位置的时候,我们也指定出口位置为墙上的一个开口,比赛结束后,我将代码优化补全,现在支持安全出口的位置在室内任何地方。
因为不是一篇写这次比赛解决方法的博客,下面我重点展示代码实现。先展示各个点的实现,最后在附送上完整代码。
运用到matplotlib库进行可视化,所以就不提基础部分,主要展示怎么将现实问题抽象成代码
1、创建房间、创建人员及初始位置生成、安全出口
N, row, column = 250, 20, 20 #室内人数,矩阵的行数,矩阵的列数
room = np.zeros((row + 2, column + 2)) # 创建房间
export = [(6, 11, 'E')] # 设定出口位置和开门方向,此处为坐标轴坐标
** 注意:**出口的坐标表示为二维直角坐标系的定位方式,并非出口在矩阵中的索引,出口方向分别为‘N’‘S’‘W’‘E’,对应四个方位。
def initial_room():
'''
生成初始人员并进行随机分配
'''
coordinates = [] # 室内人员坐标
for i in range(N):
generated_data = [random.randint(1, row), random.randint(1, column)]
if not generated_data in coordinates: # 去掉重复数据
coordinates.append(generated_data)
for i in range(len(coordinates)):
room[coordinates[i][0]][coordinates[i][1]] = 1
# 创建围墙、创建出口
for i in range(len(room)):
for j in range(len(room)):
if i == 0 or i == len(room) - 1 or j == 0 or j == len(room[i]) - 1:
room[i][j] = 9
for i in range(len(export)):
room[export[i][1]][export[i][0]] = 8
return
人员随机分配,对应矩阵中的索引,设置删除重复的位置,即得一个人对应一个位置。围墙使用数值9表示,出口使用数值8表示。
2、可视化
for i in range(1, len(room) - 1):
for j in range(1, len(room[i]) - 1):
if room[i][j] == 1:
rect = mpathes.Circle((j + 0.5, i + 0.5), 0.3, color='grey')
ax.add_patch(rect)
plt.show()
将矩阵索引与二维坐标Y轴一一对应,即矩阵索引为0的列表对应坐标轴Y方向最小的坐标,便于后续编程
代码半完整写下来将近300行,还有一些没有完善,下文会提到
import matplotlib.pyplot as plt
import matplotlib.patches as mpathes
import numpy as np
import random
# 初始位置生成
def initial_room():
'''
生成初始人员并进行随机分配
'''
coordinates = [] # 室内人员坐标
for i in range(N):
generated_data = [random.randint(1, row), random.randint(1, column)]
if not generated_data in coordinates: # 去掉重复数据
coordinates.append(generated_data)
for i in range(len(coordinates)):
room[coordinates[i][0]][coordinates[i][1]] = 1
# 创建围墙、创建出口
for i in range(len(room)):
for j in range(len(room)):
if i == 0 or i == len(room) - 1 or j == 0 or j == len(room[i]) - 1:
room[i][j] = 9
for i in range(len(export)):
room[export[i][1]][export[i][0]] = 8
return
# 创建平台
def create_flat():
# fig, ax = plt.subplots()
plt.xticks(np.arange(0, column + 3, 1.0))
plt.yticks(np.arange(0, row + 3, 1.0))
plt.grid(linestyle='--')
wall_botton = mpathes.Rectangle((0, 0), column + 2, 1, color='grey')
wall_left = mpathes.Rectangle((0, 0), 1, row + 2, color='grey')
wall_top = mpathes.Rectangle((0, row + 1), column + 2, 1, color='grey')
wall_right = mpathes.Rectangle((column + 1, 0), 1, row + 2, color='grey')
ax.add_patch(wall_botton)
ax.add_patch(wall_top)
ax.add_patch(wall_left)
ax.add_patch(wall_right)
for i in range(len(export)):
ax.add_patch(mpathes.Rectangle((export[i][0], export[i][1]), 1, 1, color='green'))
# 更新状态
def update_state():
'''
搜寻距离出口同一层的人,记录坐标
:return:
'''
# 记录坐标
tem=[]
for i in range(len(export)):
tem.append(column-export[i][0])
tem.append(row-export[i][1])
tem.append(export[i][0]-1)
tem.append(export[i][1]-1)
n = max(tem)
total_around_people = []
for i in range(1, n + 1): # 总循环次数n,从1开始
around_people = []
if export[0][1] + i < row + 1: # 上左(有这一行)在范围内
if export[0][0] - i > 0: # 上左(列)在范围内
if export[0][0] + i < column + 1: # 上右(列)在范围内
for j in range(export[0][0] - i, export[0][0] + i + 1): #列的变化范围
if room[export[0][1]+i][j] == 1:
around_people.append((export[0][1]+i, j))
else: # 上右(列)不再范围内
for j in range(export[0][0] - i, column + 1):
if room[export[0][1]+i][j] == 1:
around_people.append((export[0][1]+i, j))
else: # 上左(列)不在范围内
if export[0][0] + i < column + 1: # 上右(列)在范围内
for j in range(1, export[0][0]+i+1):
if room[export[0][1]+i][j] == 1:
around_people.append((export[0][1]+i, j))
else: # 上右(列)不再范围内
for j in range(1, column + 1):
if room[export[0][1]+i][j] == 1:
around_people.append((export[0][1], j))
if export[0][1] - i > 0: # 下左在范围内(有这一行)
if export[0][0] - i > 0: # 下左(列)在范围内
if export[0][0] + i < column + 1: # 下右(列)在范围内
for j in range(export[0][0] - i, export[0][0] + i + 1):
if room[export[0][1]-i][j] == 1:
around_people.append((export[0][1]-i, j))
else: # 下右(列)不在范围内
for j in range(export[0][0] - i, column + 1):
if room[export[0][1]-i][j] == 1:
around_people.append((export[0][1]-i, j))
else: # 下左(列)不在范围内
if export[0][0] + i < column + 1: # 下右(列)在范围内
for j in range(1, export[0][0] + i + 1):
if room[export[0][1]-i][j] == 1:
around_people.append((export[0][1]-i, j))
else: # 下右(列)不在范围内
for j in range(1, column + 1):
if room[export[0][1]-i][j] == 1:
around_people.append((export[0][1]-i, j))
if export[0][0] - i > 0: # 上左(列)存在
if export[0][1] + i < row + 1: # 上左(行)存在
if export[0][1] - i > 0: # 下左(行)存在
for j in range(export[0][1]- i +1, export[0][1] + i):
if room[j][export[0][0]-i] == 1:
around_people.append((j, export[0][0] - i))
else: # 下左(行)不存在
for j in range(1, export[0][1] + i):
if room[j][export[0][0] - i] == 1:
around_people.append((j, export[0][0] - i))
else: # 上左(行)不存在
if export[0][1] - i > 0: # 下左(行)存在
for j in range(export[0][1]+1,len(room)-1):
if room[j][export[0][0] - i] == 1:
around_people.append((j, export[0][0] - i))
else: # 下左(行)不存在
for j in range(1, len(room)-1):
if room[j][export[0][0] - i] == 1:
around_people.append((j, export[0][0] - i))
if export[0][0] + i < len(room[i])-1: # 上右(列)存在
if export[0][1] + i < len(room)-1: # 上右(行)存在
if export[0][1] - i > 0: # 下右(行)存在
for j in range(export[0][1] - i + 1, export[0][1] + i):
if room[j][export[0][0] + i] == 1:
around_people.append((j, export[0][0] + i))
else: # 下右(行)不存在
for j in range(1, export[0][1] + i):
if room[j][export[0][0] + i] == 1:
around_people.append((j, export[0][0] + i))
else: # 上右(行)不存在
if export[0][1] - i > 0: # 下右(行)存在
for j in range(export[0][1]-i, len(room)-1):
if room[j][export[0][0] + i] == 1:
around_people.append((j, export[0][0] + i))
else: # 下右(行)不存在
for j in range(1, len(room)-1):
if room[j][export[0][0] + i] == 1:
around_people.append((j, export[0][0] + i))
total_around_people.append(around_people)
newlist = [x for x in total_around_people if x] # 删除空格项
distance = []
dic = {}
for i in range(len(newlist)):
subdis = []
for j in range(len(newlist[i])):
dis = ((newlist[i][j][0] - export[0][1] + 1) ** 2 + (newlist[i][j][1] - export[0][0] + 1) ** 2)
dic[newlist[i][j]] = dis
subdis.append(dis)
distance.append(subdis)
tourists_move(dic, distance) # 引用移动函数
def tourists_move(dic, distance):
for k in range(len(distance)):
order = sorted(distance[k])
order_dis = [] # 排序存放了周围元素距离坐标
for i in order:
for key, val in dic.items():
if val == i:
order_dis.append(key)
del dic[key]
break
if export[0][2] == 'E': # 出口朝向
for i in range(len(order_dis)): # 一次判断每个点
x, y, xdoor, ydoor = order_dis[i][0], order_dis[i][1], export[0][1], export[0][0]
if x < xdoor and y > ydoor: # 右下角
if room[x+1][y-1] == 8: #斜角
room[x][y] = 0
elif room[x + 1][y - 1] == 0 and y-1>ydoor : # 斜角
room[x + 1][y - 1] = 1
room[x][y] = 0
elif room[x][y - 1] == 0: # 往左
room[x][y - 1] = 1
room[x][y] = 0
elif room[x + 1][y] == 0: # 往下
room[x + 1][y] = 1
room[x][y] = 0
else:
continue
elif x > xdoor and y > ydoor: # 右上角
if room[x-1][y-1]==8:
room[x][y] = 0
elif room[x - 1][y - 1] == 0 and y-1>ydoor: # 斜角
room[x - 1][y - 1] = 1
room[x][y] = 0
elif room[x][y - 1] == 0: # 往左
room[x][y - 1] = 1
room[x][y] = 0
elif room[x - 1][y] == 0: # 往下
room[x - 1][y] = 1
room[x][y] = 0
else:
continue
elif x == xdoor and y > ydoor: #(右)正前面
if room[x][y - 1] == 8: #往前8
room[x][y] = 0
elif room[x][y - 1] == 0: #往前
room[x][y - 1] = 1
room[x][y] = 0
else:
if room[x+1][y-1]==0:
room[x + 1][y - 1] = 1
room[x][y]=0
elif room[x-1][y-1]==0:
room[x-1][y-1]=1
room[x][y]=0
else:
continue
elif x < xdoor and y <= ydoor: #左下
if room[x + 1][y + 1] == 0: #斜角
room[x + 1][y + 1] = 1
room[x][y] = 0
elif room[x + 1][y] == 0: #往上
room[x + 1][y] = 1
room[x][y] = 0
elif room[x][y + 1] == 0: #往右
room[x][y + 1] = 1
room[x][y] = 0
else:
continue
elif x > xdoor and y <= ydoor: #左上
if room[x - 1][y + 1] == 0: #斜角
room[x - 1][y + 1] = 1
room[x][y] = 0
elif room[x][y + 1] == 0: #往右
room[x][y + 1] = 1
room[x][y] = 0
elif room[x - 1][y] == 0: #往下
room[x - 1][y] = 1
room[x][y] = 0
else:
continue
# elif x == xdoor and y
else: #(左)正对
if room[x][y+1]==0:
room[x][y+1]=1
room[x][y]=0
else:
if room[x+1][y+1]==0:
room[x+1][y+1]=1
room[x][y]=0
elif room[x-1][y+1]==0:
room[x-1][y+1]=1
room[x][y]=0
else:
continue
elif export[2] == 'W': # 出口朝向
continue
elif export[2] == 'N': # 出口朝向
continue
elif export[2] == 'S': # 出口朝向
continue
# 可视化
def visualization():
for i in range(1, len(room) - 1):
for j in range(1, len(room[i]) - 1):
if room[i][j] == 1:
rect = mpathes.Circle((j + 0.5, i + 0.5), 0.3, color='grey')
ax.add_patch(rect)
plt.show()
def main():
create_flat()
#visualization() # 调用可视化函数
update_state()
visualization() #调用可视化函数
if __name__ == '__main__':
'''
N:室内游客总数
column:室内水平方向元胞个数,代表列数量
row:室内竖直方向元胞个数,代表行数量
'''
N, row, column = 5, 20, 20
room = np.zeros((row + 2, column + 2)) # 创建房间
export = [(0, 11, 'E')] # 设定出口位置和开门方向,此处为坐标轴坐标
initial_room() # 创建人员
while 1 in room:
fig, ax = plt.subplots()
main()
1、目前只完成了出口方向面朝东面的编程
2、对出口正对上下左右四个方向的逻辑设计不够严谨与完美,具体感兴趣的朋友可以复制完整代码运行查看,有好想法的朋友可以交流下哈哈哈哈
美赛结束之后,回家过年各种事情在忙,期间利用碎片时间来整理代码,这个结束也很仓促,想深入了解,交流的朋友可以留言,非常欢迎~~~~