题目

课堂上老师跟我们讲了这个问题,如何使用Excel进行手动仿真(其实只是讲了一些逻辑而已~):

某一排队系统,假定顾客随机地分别以1-8min(精度取到分钟)的间隔到达,到达间隔时间值为等概率出现。服务时间为1-6min(精度取到分钟),其出现的概率分别为0.10,0.20,0.30,0.25,0.10,0.05。为了说明问题,这里我们仅仿真10个顾客作为运行长度。并且忽略初始条件的影响。

分析(搬的课件)

等概率估算到达时间间隔~

顾客到达的时间间隔为1~8分钟,各时间出现概率为等概率,因此,其概率为
P(x)=1/8=0.125, x=1,2,3,4,5,6,7,8
等概率的问题就是一种均匀分布,可以通过均匀分布随机数的产生来处理。

这里直接使用1000,你们说,这是为什么呢?!

哈哈哈,当然是因为等精度啦。

不同概率估算服务时间

服务时间到达的概率分别为:0.10,0.20,0.30,0.25,0.10,0.05,对应着1-6min,这里的精度只到5%,理论上来说只要20个数字就可以完成随机概率取值。但是为了精度,精度~

仿真初始条件

这里需要预设初始条件,才能进行计算,而初始条件很简单,即第一个顾客的等待时间,开始服务时间,到达时间,到达间隔都为0就是啦。

仿真逻辑

仿真逻辑就是下面图示这样子了。

但,作业是这样的

老师:咳咳,那个,这个题目很简单了,你们就用Excel手动仿真吧~
我们:哦耶,这还不简单?啥都有了,只要重新算一遍,又不复杂,Excel拉公式走起~

然后,这个时候。。。老师。。。

老师:这样吧,我把条件改一改,就 400 个顾客,然后仿真 100 次吧!!!
我们:嗯嗯,好啊,反正只要拉公式。。。慢着,,,你说100次???

TM…

真不是人干的活~

解决方案

那没办法,只好请出还没学的 Python 了,操作Excel文件比起 C++ 可友好多了,只是,还没学,咋办呢~

学!

这里选用的是 xlsxwriter ,也就是“ xlsx writer ”联合体,专门处理 xlsx 文件,这个得pip install一波。然后还使用到了 pandas ,一个很优秀的数据分析工具,但其实可以不用这个。。。

于是开始码代码(有点点长):

运行环境:Python 3.7
IDE:Pycharm
2019.9.23新版更新,增加总体分析统计表输出,并在每一张分表加入分析数据
效果如下图片所示:

分析直方图

分表分析数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
import xlsxwriter
import pandas as pd
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import os

# 创建新的 xlsx 文件,名字记得更改
workbook = xlsxwriter.Workbook('Modeling_Qiu.xlsx')

# 实验次数
times = 100

# 顾客数量
customer = 401

# 统计数据,主要收集等待时间,以及每个区间的数量
data_flow1 = [['等待时间区间', '等待频数'], ['0-0.5', 0], ['0.5-1', 0], ['1-1.5', 0], ['1.5-2', 0], ['2-2.5', 0], ['2.5-3', 0],
['3-3.5', 0], ['3.5-4', 0], ['4-4.5', 0], ['4.5-', 0]]

# 循环创建100个工作表,并输入数据计算
for exp in range(0, times):

# 等待的顾客数量
wait_customer = 0

# 创建工作表
worksheet = workbook.add_worksheet()

# 设置工作表的列宽为20
worksheet.set_column(0, 20, 20)

# 设置字体
# format_products = workbook.add_format({'font_name': 'Times New Roman'})

# 设定整个sheet表的单元格的格式
property = {
'font_size': 16, # 字体大小
'bold': False, # 加粗
'align': 'center', # 水平对齐方式
'valign': 'vcenter', # 垂直对齐方式
'font_name': u'楷体',
'text_wrap': False, # 是否自动换行
}
cell_format = workbook.add_format(property)

# 在向单元格中写入内容时,加上单元格样式
# ws.write(row, col, data, cell_format)

# 统计数据,每一张分表的数据
data_flow2 = [['等待时间区间', '等待频数'], ['0-0.5', 0], ['0.5-1', 0], ['1-1.5', 0], ['1.5-2', 0], ['2-2.5', 0],
['2.5-3', 0],
['3-3.5', 0], ['3.5-4', 0], ['4-4.5', 0], ['4.5-', 0]]

# 输入数据
df = pd.DataFrame({'顾客': [], '到达时间间隔': [], '服务时间': [], '到达时刻': [], '服务开始时刻': [], '服务结束时刻': [], '等待时间': [], '逗留时间': [], '服务员空闲时间': []})

# worksheet.write 函数写入第一行列名,参数分别表示行、列、数据、数据格式。
for col in range(len(df.columns)):
worksheet.write(0, col, df.columns[col], cell_format)

# 生成一个 9 列 customer 行的随机数组
data = [[np.random.randint(0, 1000) for j in range(1, 10)] for i in range(1, customer + 1)]

# 处理仿真表格数据
for i in range(1, customer):

# 设置行高
worksheet.set_row(i-1, 30)

# 顾客序号
data[i-1][0] = i

# 顾客到达间隔
if i==1 :
data[i-1][1] = 0
else :
temp = data[i-1][1] / 125
data[i-1][1] = int(temp) + 1

# 顾客服务时间
if data[i-1][2] < 100:
data[i-1][2] = 1
elif data[i-1][2] < 300:
data[i-1][2] = 2
elif data[i-1][2] < 600:
data[i-1][2] = 3
elif data[i-1][2] < 850:
data[i-1][2] = 4
elif data[i-1][2] < 950:
data[i-1][2] = 5
else:
data[i-1][2] = 6

# 到达时刻 = 到达时间间隔 + 上一次顾客到达时刻
if i == 1:
data[i - 1][3] = 0
else:
data[i - 1][3] = data[i - 2][3] + data[i - 1][1]

# 服务开始时刻 = max(上一次服务结束时刻,本次到达时刻)
if i == 1:
data[i-1][4] = 0
else:
data[i-1][4] = max(data[i-2][5], data[i-1][3])

# 服务结束时间 = 服务开始时间 + 服务时间
data[i-1][5] = data[i-1][4] + data[i-1][2]

# 等待时间 = 服务开始时刻 - 到达时刻
data[i-1][6] = data[i-1][4] - data[i-1][3]
if data[i-1][6] > 0:
wait_customer += 1

# 逗留时间 = 顾客在系统中的时间 = 等待时间 + 服务时间
data[i - 1][7] = data[i-1][6] + data[i-1][2]

# 服务员空闲时间 = 本次服务开始时间 - 上次服务结束时间
# 如果 > 0,服务员空闲时间 = 该值
# 其他,服务员空闲时间 = 0
if i == 1:
data[i - 1][8]=0
else:
if data[i-1][4] - data[i-2][5] > 0:
data[i - 1][8] = data[i-1][4] - data[i-2][5]
else:
data[i - 1][8] = 0

# 统计等待时间频次
if data[i - 1][6] < 0.5:
data_flow2[1][1] += 1
elif data[i - 1][6] < 1:
data_flow2[2][1] += 1
elif data[i - 1][6] < 1.5:
data_flow2[3][1] += 1
elif data[i - 1][6] < 2:
data_flow2[4][1] += 1
elif data[i - 1][6] < 2.5:
data_flow2[5][1] += 1
elif data[i - 1][6] < 3:
data_flow2[6][1] += 1
elif data[i - 1][6] < 3.5:
data_flow2[7][1] += 1
elif data[i - 1][6] < 4:
data_flow2[8][1] += 1
elif data[i - 1][6] < 4.5:
data_flow2[9][1] += 1
else:
data_flow2[10][1] += 1

# 最后一行统计数据,累加计算
for j in range(1, 10):
data[400][j-1] = 0
for i in range(1, customer):
data[400][1] += data[i-1][1]
data[400][2] += data[i-1][2]
data[400][6] += data[i-1][6]
data[400][7] += data[i-1][7]
data[400][8] += data[i-1][8]

# 最后一行表头,记得改掉 ID
data[400][0] = '累加结果'

# 生成一个 7 列 2 行的随机数组,作为储存分析表的数据
data_chart = [['顾客平均等待时间', '顾客必须等待概率', '服务台空闲概率', '平均服务时间', '平均到达间隔', '平均等待时间', '平均花费时间'],
['0', '0', '0', '0', '0', '0', '0']]

# 计算分析表数据
data_chart[1][0] = data[400][6] / (customer - 1)
data_chart[1][1] = wait_customer / (customer - 1)
data_chart[1][2] = data[400][8] / data[399][5]
data_chart[1][3] = data[400][2] / (customer - 1)
data_chart[1][4] = data[400][1] / (customer - 1)
data_chart[1][5] = data[400][6] / (customer - 2)
data_chart[1][6] = data[400][7] / (customer - 1)

# 统计等待时间频次
if data_chart[1][0] < 0.5:
data_flow1[1][1] += 1
elif data_chart[1][0] < 1:
data_flow1[2][1] += 1
elif data_chart[1][0] < 1.5:
data_flow1[3][1] += 1
elif data_chart[1][0] < 2:
data_flow1[4][1] += 1
elif data_chart[1][0] < 2.5:
data_flow1[5][1] += 1
elif data_chart[1][0] < 3:
data_flow1[6][1] += 1
elif data_chart[1][0] < 3.5:
data_flow1[7][1] += 1
elif data_chart[1][0] < 4:
data_flow1[8][1] += 1
elif data_chart[1][0] < 4.5:
data_flow1[9][1] += 1
else:
data_flow1[10][1] += 1

# worksheet.write 函数写入第一行列名,参数分别表示行、列、数据、数据格式。
for i in range(1, customer+1):
for j in range(1, 10):
worksheet.write(i, j - 1, data[i-1][j-1], cell_format)

# 写入表格
for i in range(1, 3):
for j in range(1, 8):
worksheet.write(i - 1, j + 9, data_chart[i - 1][j - 1], cell_format)

# 写入表格
for i in range(1, 12):
for j in range(1, 3):
worksheet.write(i + 3, j + 9, data_flow2[i-1][j-1], cell_format)

# 测试代码
# print(np.array(data))
# print(np.array(data_chart))

# 工具图片
# my_file = '‪D:/pycode/Pycharm/temp.png'
# shutil.rmtree('my_file')
# if os.path.exists('temp.png'):
# os.remove('temp.png')

# 开始画图
mpl.use('Agg')

# 图表基本信息
font_size = 12 # 字体大小
fig_size = (8, 6) # 图表大小

# 图例名称
names = (r'Times') # 姓名

# 更新字体大小
mpl.rcParams['font.size'] = font_size
# 更新图表大小
mpl.rcParams['figure.figsize'] = fig_size
# 设置柱形图宽度
bar_width = 1.0

# 绘制「小明」的成绩
data_flow2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
data_flow3 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
index = np.arange(len(data_flow2))
for i in range(1, 11):
data_flow2[i-1] = data_flow1[i][1]
data_flow3[i-1] = data_flow1[i][0]
rects1 = plt.bar(index, data_flow2, bar_width, color='#0072BC', label=names[0])

# 测试代码
# print(data_flow1)

# X轴标题
plt.xticks(index, data_flow3)

# y轴范围,最高100,最低0
plt.ylim(ymax=100, ymin=0)
# 图表标题
plt.title(r'Average Waiting Times of Customers, Made by Python')
# 图例显示在图表下方
plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.03), fancybox=True, ncol=50)

# 添加数据标签
def add_labels(rects):
for rect in rects:
height = rect.get_height()
plt.text(rect.get_x() + rect.get_width() / 2, height, height, ha='center', va='bottom')

# 柱形图边缘用白色填充,纯粹为了美观
rect.set_edgecolor('white')

# 添加数据标签,100次实验数据
add_labels(rects1)

# 图表输出到本地
plt.savefig('Modling-All-Analyse.png')

# 图片插入到 N5
# worksheet.insert_image('N5', 'times.png')

# 关闭保存
workbook.close()

Over,大功告成!