多路电梯算法调度的解决思路——类设计角度

这学期C++接触得有点多,为熟练类设计,做了一个多路电梯调度算法。

总的来说,这份代码主要包含调度核心算法、乘客行为设计、电梯动作函数设计等,最终完成了一个十路电梯的群控仿真算法系统。同时为了增强了整个电梯系统的控制简便性和高效性,还将电梯进行分组。也设计了不同时间段(早晚高峰、平时)的算法仿真流程。

OPP其实是一种思想,所以本博客的核心还是在于边写边继续加深面向对象编程的理解吧。

整体代码框架

整体调度思路

总的来说,调度算法采用了短寻找楼层时间优先(SSTF-Shortest Seek Time First)算法,其分为内部调度外部调度两个部分,最终形成每部电梯的任务库,每部电梯根据自己的任务库进行合理运行。

内部调度中,电梯内部的楼层按钮被按下时,该任务即被送入此电梯的任务库中,等待被完成。

外部调度则较为复杂,总体上来看,外部调度时通过计算电梯与外部请求发生的楼层距离,选择合理的、距离最近的电梯响应请求。如果所有的电梯都不是“合理的”,那么该请求被送入等待队列。其中,电梯的状态分为三类:1. 空闲电梯(“电梯空闲”状态)2. 上升电梯(“上升”状态和“上升中停靠”状态)3. 下降电梯(“下降”状态和“下降中停靠”状态)当外部请求发生时,调度算法将根据请求发生的楼层和请求的方向(向上或向下)从上面的三种状态的电梯中选择两种,从里面进行挑选。其中,“空闲电梯”是会一直被选中的,剩下一种要根据请求的方向确定,上升的请求就选中“上升电梯”,下降的请求就选中“下降电梯”。

确定候选电梯后需要对选出的可行电梯进行进一步筛选,筛选的原则是根据当前电梯的位置和乘客的位置赋予每部可行电梯一个权重,权重综合代表电梯和乘客之间的距离以及电梯目前的承载人数等,权重越大代表该部电梯越能快速接到该用户,最后将该乘客送入权重最大的电梯的任务库。

电梯类的状态转移图大致为:

具体代码

主函数Main.cpp

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
// lift.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include "ElevatorLift.h"
#include <time.h>
#include "Passenger.h"
#include "Group.h"
using namespace std;
//设定全局变量
ElevatorLift * E[10]; //电梯指针
Passenger * P[1000]; //乘客指针
Group * G[5]; //电梯组指针
Time Nowtime;
int K = 12; //电梯的最大载客数量 10<K<18
int N = 400; //乘客数量 0<N<1000
int M = 1; //初始等待时间 (分钟) 0<M<10
int L = 5; //乘客的乘梯次数 1<L<10
int S = 1; //电梯速度 单位 :秒/层 1<s<5
int T = 1; //乘客上下电梯的时间 2<T<10
void GuideInput();
void Show();
void over();
void summary();
bool IsOver();

//主函数
int main()
{
srand((int)time(0));
GuideInput();
SYSTEMTIME NowTime;
GetLocalTime(&NowTime);
Nowtime.set(NowTime.wHour, NowTime.wMinute, NowTime.wSecond);
E[0] = new ElevatorLift(S, K, T, &Nowtime);
E[1] = new ElevatorLift(S, K, T, &Nowtime);
E[2] = new E1(S, K, T, &Nowtime);
E[3] = new E1(S, K, T, &Nowtime);
E[4] = new E2(S, K, T, &Nowtime);
E[5] = new E2(S, K, T, &Nowtime);
E[6] = new E3(S, K, T, &Nowtime);
E[7] = new E3(S, K, T, &Nowtime);
E[8] = new E4(S, K, T, &Nowtime);
E[9] = new E4(S, K, T, &Nowtime);
for (int i = 0; i < 5; i++) //10台电梯分为5组
{
G[i] = new Group(E[2*i], E[2*i + 1]);
}
for (int i = 0; i < N; i++) //所有乘客设置为Passenger类
{
P[i] = new Passenger(M, L, E, &Nowtime);
}

VOID CALLBACK myTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
MSG msg;
//设定定时器,定时执行任务
SetTimer(NULL, 1, 2000, (TIMERPROC)myTimerProc);
bool flag = true;
//主循环
while (flag)
{
if (IsOver())
{
flag = false;
}
Show();
if (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

}
KillTimer(NULL, 1);
summary();
return 0;

}

VOID CALLBACK myTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
Nowtime = Nowtime + 1;
//电梯组行动
for (int i = 0; i < 5; i++)
{
G[i]->action();
}
// 乘客活动
for (int i = 0; i < N; i++)
{
P[i]->action();
}
}

void GuideInput()
{
cout << "请输入乘客数量:" << endl;
cin >> N;
cout << "请输入电梯的最大载客数量:" << endl;
cin >> K;
cout << "请输入初始等待时间:" << endl;
cin >> M;
cout << "请输入乘客的乘梯次数:" << endl;
cin >> L;
cout << "请输入电梯的速度(单位:秒/层):" << endl;
cin >> S;
cout << "请输入每名乘客上下电梯的时间 :" << endl;
cin >> T;
}

void Show()
{ system("cls");
cout << "当前时间" << endl;
printf("%d:%d:%d\n", Nowtime.hour, Nowtime.minute, Nowtime.second);

printf("乘客ID 当前状态 当前楼层 目标楼层 组号 电梯号 剩余乘梯次数 \n");
for (int i = 0; i < N; i++)
{
printf("%2d %s %2d %2d %2d %2d %2d\n"
, i + 1, P[i]->GetStatus(), P[i]->GetNowFloor(), P[i]->GetNextFloor(), P[i]->Getgroup(), P[i]->Getwhich(), P[i]->Gettimes());
//printf(" 下一次乘梯时间:%d:%d:%d\n ", P[i]->NextTime.hour, P[i]->NextTime.minute, P[i]->NextTime.second);

}

printf("-------------------------------------------------------------------\n");
printf("电梯号 当前状态 当前楼层 目标楼层 当前人数:\n");
for (int i = 0; i < 10; i++)
{
printf(" %2d %4s %2d %2d %2d\n"
, i + 1, E[i]->GetStatus(), E[i]->GetNowFloor(), E[i]->GetNextFloor(), E[i]->Getpeople());

}

}
void over()
{
system("cls");

for (int i = 0; i < 10; i++)
{
printf("%d号电梯:\n当前状态:%s 当前楼层:%d 目标楼层:%d 当前人数:%d\n", i + 1, E[i]->GetStatus(), E[i]->GetNowFloor(), E[i]->GetNextFloor(), E[i]->Getpeople());
printf("-------------------------------------------------------------------\n");
}
for (int i = 0; i < N; i++)
{
printf("%d号乘客:\n当前状态: %s 当前楼层: %d 目标楼层:%d 次数:%d\n", i, P[i]->GetStatus(), P[i]->GetNowFloor(), P[i]->GetNextFloor(), P[i]->Gettimes());
printf("组号:%d ,电梯号 :%d\n", P[i]->Getgroup(), P[i]->Getwhich());
printf(" 下一次乘梯时间:%d:%d:%d\n ", P[i]->NextTime.hour, P[i]->NextTime.minute, P[i]->NextTime.second);
printf("**********************************************************************\n");
}
}
void summary()
{
system("cls");
cout << "当前时间" << endl;
printf("%d:%d:%d\n", Nowtime.hour, Nowtime.minute, Nowtime.second);
printf("电梯号 忙碌时间(秒) 空闲时间(秒) \n");
for (int i = 0; i < 10; i++)
{

printf("%2d %3d %3d\n", i + 1, E[i]->Getbusy(), E[i]->Getvacant());

}
printf("**********************************************************************\n");
printf("乘客ID 等待时间(秒) \n");
for (int i = 0; i < N; i++)
{
printf("%2d %3d\n", i + 1, P[i]->GetTotal());
}

}
bool IsOver()
{
for (int i = 0; i < N; i++)
{
if (P[i]->getstatus() !=3 )
{
return false;
}
}
return true;
}

电梯类

ElevatorLift.h

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
#pragma once
#include <Windows.h>
#include "Time.h"
constexpr auto Max = 40; //楼层数目

class ElevatorLift
{
public:
ElevatorLift();
ElevatorLift(int S, int K, int T, Time *tm);
~ElevatorLift();
void action();
void UpdateNowFloor(); //更新当前电梯所在楼层
void ChangeStatus(); //改变电梯运行状态
void Run(); //电梯运行
void UpdateDestination(); //更新下一站的目的楼层
int call(); //查看是否有人呼叫电梯
int IsTake(); //
char * GetStatus(); //得到现在状态函数
int GetNextFloor();
int GetNowFloor();
int Getpeople();
int Getbusy();
int Getvacant();
virtual void SetUp(int floor, int n);
virtual void SetDown(int floor, int n);
virtual void SetInsideUp(int floor, int n);
virtual void SetInsideDown(int floor, int n);
protected:
enum Status // 电梯状态
{
STOP, // 停止
UP, // 向上运行
DOWN, // 向下运行
UPSTOP, // 上行过程中的暂停
DOWNSTOP // 下行过程中的暂停
};
Status status;
int NextFloor; //电梯下一次需要停留的楼层
int NowFloor; //当前电梯所在楼层
int Up[Max]; //电梯外部,记录乘客上行呼叫的楼层 Up[i]的数值代表第i层上行的呼叫电梯的乘客数量
int Down[Max]; //电梯外部,记录乘客下行呼叫的楼层 Down[i]的数值代表第i层下行的呼叫电梯的乘客数量
int InsideUp[Max]; //电梯内部,设置乘客上行目的楼层的数组
int InsideDown[Max]; //电梯内部,设置乘客下行目的楼层的数组
int people; //当前电梯的人数
int capacity; //当前电梯的容量
float nowfloor; //用于计算当前楼层
float speed; //电梯当前运行的速度
long vacant; //电梯空闲时间
long busy; //电梯运行的时间
Time NextStart; //电梯停靠以后下一次的启动时间
Time Start; //电梯启动的时间
Time Over; //电梯停止的时间
Time * nowtime; //当前时间指针
unsigned int StopTime; //电梯停靠的时间
friend class Passenger; //将自身数据提供给乘客
char str[10]; //用于存放当前状态的

};

class E1 :public ElevatorLift //25-40楼层的电梯
{
public:

E1(int S, int K, int T, Time *tm);
void SetUp(int floor, int n);
void SetDown(int floor, int n);
void SetInsideUp(int floor, int n);
void SetInsideDown(int floor, int n);
};
class E2 :public ElevatorLift //1-25楼层的电梯
{
public:
E2(int S, int K, int T, Time *tm);
void SetUp(int floor, int n);
void SetDown(int floor, int n);
void SetInsideUp(int floor, int n);
void SetInsideDown(int floor, int n);
};
class E3 :public ElevatorLift //偶数楼层的电梯
{
public:
E3(int S, int K, int T, Time *tm);
void SetUp(int floor, int n);
void SetDown(int floor, int n);
void SetInsideUp(int floor, int n);
void SetInsideDown(int floor, int n);
};
class E4 :public ElevatorLift //奇数楼层的电梯
{
public:
E4(int S, int K, int T, Time *tm);
void SetUp(int floor, int n);
void SetDown(int floor, int n);
void SetInsideUp(int floor, int n);
void SetInsideDown(int floor, int n);
};

ElevatorLift.cpp

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
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
//#include "pch.h"
#include "ElevatorLift.h"
#include <math.h>
#include <time.h>
ElevatorLift::ElevatorLift()
{
}
ElevatorLift::ElevatorLift(int S, int K, int T, Time *tm)
{
status = STOP; //初始电梯停止
NowFloor = (rand() % (40 - 1)) + 1; //随机生成初始电梯停靠位置
NextFloor = NowFloor; //电梯停止时目标楼层与当前楼层一致
nowfloor = NowFloor; //用于计算的位置
speed = 1 / (float)S; //设置速度 S的单位为秒/层 speed 单位为 层/秒
StopTime = T; //每个乘客上下电梯所需时间
capacity = K; //电梯的容量
people = 0; //当前电梯的人数
vacant = 0; //电梯空闲时间
busy = 0; //电梯运行时间
nowtime = tm;
Over = *nowtime;
//初始化
for (int i = 0; i < 41; i++)
{
Up[i] = 0;
Down[i] = 0;
InsideUp[i] = 0;
InsideDown[i] = 0;
}

}


ElevatorLift::~ElevatorLift()
{
}

void ElevatorLift::action() // 电梯行为
{
UpdateNowFloor(); // 更新当前电梯所在楼层
UpdateDestination(); // 更新电梯下一站的目的楼层
ChangeStatus(); // 判断改变当前电梯状态
Run(); // 电梯运行
}

char * ElevatorLift::GetStatus()
{

switch (status)
{
case UP:
strcpy_s(str, "上");
break;
case UPSTOP:
strcpy_s(str, "上停");
break;
case DOWN:
strcpy_s(str, "下");
break;
case DOWNSTOP:
strcpy_s(str, "下停");
break;
case STOP:
strcpy_s(str, "停");
break;
default:
break;
}
return str;
}

int ElevatorLift::GetNextFloor()
{
return NextFloor;
}

int ElevatorLift::GetNowFloor()
{
return NowFloor;
}

int ElevatorLift::Getpeople()
{
return people;
}

int ElevatorLift::Getbusy()
{
return busy;
}

int ElevatorLift::Getvacant()
{
return vacant;
}

void ElevatorLift::SetUp(int floor, int n)
{
Up[floor] += n;
}

void ElevatorLift::SetDown(int floor, int n)
{
Down[floor] += n;
}

void ElevatorLift::SetInsideUp(int floor, int n)
{
InsideUp[floor] += n;
}

void ElevatorLift::SetInsideDown(int floor, int n)
{
InsideDown[floor] += n;
}


void ElevatorLift::UpdateNowFloor()
{
NowFloor = (int)nowfloor;
}
void ElevatorLift::UpdateDestination()
{
switch (status)
{
case UP:
for (int i = NowFloor; i < Max; i++) //从当前楼层上一层开始查询,如果有停靠记录,取最近的楼层为目标楼层
{
if (Up[i] || InsideUp[i])
{
NextFloor = i;
break;
}
}
break;
case UPSTOP:
for (int i = NowFloor; i < Max; i++) //从当前楼层开始查询,如果有停靠记录,取最近的楼层为目标楼层
{
if (Up[i] || InsideUp[i] || Down[i])
{
NextFloor = i;
break;
}
}
break;
case DOWN:
for (int i = NowFloor; i > 0; i--) //从当前楼层开始查询,如果有停靠记录,取最近的楼层为目标楼层
{
if (Down[i] || InsideDown[i])
{
NextFloor = i;
break;
}
}
break;
case DOWNSTOP:
for (int i = NowFloor; i > 0; i--) //从当前楼层开始查询,如果有停靠记录,取最近的楼层为目标楼层
{
if (Down[i] || InsideDown[i] || Up[i])
{
NextFloor = i;
break;
}
}
break;
case STOP:


if (IsTake()) //如果电梯停下来,以后还有目标楼层
{
NextFloor = IsTake();
}
else
{ //没人乘坐的话,查询呼叫
if (call()) //如果有人呼叫电梯
NextFloor = call();
}
break;
default:
break;
}

}

void ElevatorLift::ChangeStatus()
{
switch (status)
{
case STOP: //状态为停止,则检查是否改变状态到“上”或“下”

if (NextFloor != NowFloor) //如果发现目标楼层与当前楼层不一致,则需要启动电梯
{
if (NextFloor - NowFloor > 0)
{
status = UP;
vacant += (*nowtime - Over);
Start = *nowtime;
}
else
{
status = DOWN;
vacant += (*nowtime - Over);
Start = *nowtime;
}
}
break;
case UP: //状态为向上运行,则检查是否改变状态到“上停”
if (NextFloor == NowFloor) //如果到达停靠楼层
{
status = UPSTOP;
NextStart = (*nowtime + (1 + Up[NowFloor] + InsideUp[NowFloor])*StopTime); //计算停留时间
InsideUp[NowFloor] = 0; //乘客上下电梯

}
break;
case UPSTOP:
if (NextStart.hour == nowtime->hour&&NextStart.minute == nowtime->minute&&NextStart.second == nowtime->second)
{
if (NextFloor > NowFloor)
{
status = UP;
}
else
{
status = STOP;
busy += (*nowtime - Start);
Over = *nowtime;
}
}
break;
case DOWN:
if (NextFloor == NowFloor) //如果到达停靠楼层
{
status = DOWNSTOP;
NextStart = (*nowtime + (1 + Down[NowFloor] + InsideDown[NowFloor])*StopTime); //计算停留时间
InsideDown[NowFloor] = 0;
}
break;
case DOWNSTOP:

if (NextStart.hour == nowtime->hour&&NextStart.minute == nowtime->minute&&NextStart.second == nowtime->second)
{
if (NextFloor >= NowFloor)
{
status = STOP;
busy += (*nowtime - Start);
Over = *nowtime;
}
else
{
status = DOWN;
}
}
break;

default:
break;
}
}

void ElevatorLift::Run()
{


if (status == UP) //如果电梯向上运行
{

nowfloor += speed; //speed为电梯每秒的运行速度
//int temp = 0;
//for (int i = NowFloor; i < 41; i++)
//{
// temp += InsideUp[i];
//}//统计人数
//people = temp;
}
else if (status == DOWN) //向下运行
{
nowfloor -= speed;//
//int temp = 0;
//for (int i = NowFloor; i > 0; i--)
//{
// temp += InsideDown[i];
//}//统计人数
//people = temp;
}
else if (status == UPSTOP)
{
//int temp = 0;
//for (int i = NowFloor; i < 41; i++)
//{
// temp += InsideUp[i];
//}//统计人数
//people = temp; // 1.电梯制动
// 2.使电梯位置与楼层地面相平
}
else if (status == DOWNSTOP) //向下运行
{
//int temp = 0;
//for (int i = NowFloor; i > 0; i--)
//{
// temp += InsideDown[i];
//}//统计人数
//people = temp;
}
else
{

}

}


int ElevatorLift::call()
{
for (int i = 1; i < Max; i++)
{
if (Up[i] || Down[i])
return i;
}
return 0;
}

int ElevatorLift::IsTake()
{
for (int i = 1; i < Max; i++)
{
if (InsideUp[i] || InsideDown[i])
return i;
}
return 0;
}





E1::E1(int S, int K, int T, Time * tm)
{
status = STOP; //初始电梯停止
NowFloor = (rand() % (40 - 1)) + 1; // 随机生成电梯停靠位置
NextFloor = NowFloor; //电梯停止时目标楼层与当前楼层一致
nowfloor = NowFloor; //用于计算的位置
speed = 1 / (float)S; //设置速度 s的单位为秒/层 speed 单位为 层/秒
StopTime = T; // 每个乘客上下电梯的时间
capacity = K; //电梯的容量
people = 0; //当前电梯的人数
vacant = 0; //电梯空闲时间
busy = 0; //电梯运行时间
nowtime = tm;
Over = *nowtime;
//初始化
for (int i = 0; i < 41; i++)
{

Up[i] = 0;
Down[i] = 0;
InsideUp[i] = 0;
InsideDown[i] = 0;
}
}

E2::E2(int S, int K, int T, Time * tm)
{
status = STOP; //初始电梯停止
NowFloor = (rand() % (40 - 1)) + 1; // 随机生成电梯停靠位置
NextFloor = NowFloor; //电梯停止时目标楼层与当前楼层一致
nowfloor = NowFloor; //用于计算的位置
speed = 1 / (float)S; //设置速度 s的单位为秒/层 speed 单位为 层/秒
StopTime = T; // 每个乘客上下电梯的时间
capacity = K; //电梯的容量
people = 0; //当前电梯的人数
vacant = 0; //电梯空闲时间
busy = 0; //电梯运行时间
nowtime = tm;
Over = *nowtime;
//初始化
for (int i = 0; i < 41; i++)
{

Up[i] = 0;
Down[i] = 0;
InsideUp[i] = 0;
InsideDown[i] = 0;
}
}

E3::E3(int S, int K, int T, Time * tm)
{
status = STOP; //初始电梯停止
NowFloor = (rand() % (40 - 1)) + 1; // 随机生成电梯停靠位置
NextFloor = NowFloor; //电梯停止时目标楼层与当前楼层一致
nowfloor = NowFloor; //用于计算的位置
speed = 1 / (float)S; //设置速度 s的单位为秒/层 speed 单位为 层/秒
StopTime = T; // 每个乘客上下电梯的时间
capacity = K; //电梯的容量
people = 0; //当前电梯的人数
vacant = 0; //电梯空闲时间
busy = 0; //电梯运行时间
nowtime = tm;
Over = *nowtime;
//初始化
for (int i = 0; i < 41; i++)
{

Up[i] = 0;
Down[i] = 0;
InsideUp[i] = 0;
InsideDown[i] = 0;
}
}

E4::E4(int S, int K, int T, Time* tm)
{
status = STOP; //初始电梯停止
NowFloor = (rand() % (40 - 1)) + 1; // 随机生成电梯停靠位置
NextFloor = NowFloor; //电梯停止时目标楼层与当前楼层一致
nowfloor = NowFloor; //用于计算的位置
speed = 1 / (float)S; //设置速度 s的单位为秒/层 speed 单位为 层/秒
StopTime = T; // 每个乘客上下电梯的时间
capacity = K; //电梯的容量
people = 0; //当前电梯的人数
vacant = 0; //电梯空闲时间
busy = 0; //电梯运行时间
nowtime = tm;
Over = *nowtime;
//初始化
for (int i = 0; i < 41; i++)
{

Up[i] = 0;
Down[i] = 0;
InsideUp[i] = 0;
InsideDown[i] = 0;
}
}
void E1::SetUp(int floor, int n)
{
if ((floor <= 40 && 25 <= floor) || floor == 1)
{
Up[floor] += n;
}
}

void E1::SetDown(int floor, int n)
{
if ((floor <= 40 && 25 <= floor) || floor == 1)
{
Down[floor] += n;
}
}

void E1::SetInsideUp(int floor, int n)
{
if ((floor <= 40 && 25 <= floor) || floor == 1)
{
InsideUp[floor] += n;
}
}

void E1::SetInsideDown(int floor, int n)
{
if ((floor <= 40 && 25 <= floor) || floor == 1)
{
InsideDown[floor] += n;
}
}

void E2::SetUp(int floor, int n)
{
if (floor <= 25 && 1 <= floor)
{
Up[floor] += n;
}
}

void E2::SetDown(int floor, int n)
{
if (floor <= 25 && 1 <= floor)
{
Down[floor] += n;
}
}

void E2::SetInsideUp(int floor, int n)
{
if (floor <= 25 && 1 <= floor)
{
InsideUp[floor] += n;
}
}

void E2::SetInsideDown(int floor, int n)
{
if (floor <= 25 && 1 <= floor)
{
InsideDown[floor] += n;
}
}

void E3::SetUp(int floor, int n)
{
if (floor % 2 == 0 || floor == 1)
{
Up[floor] += n;
}
}

void E3::SetDown(int floor, int n)
{
if (floor % 2 == 0 || floor==1)
{
Down[floor] += n;
}
}

void E3::SetInsideUp(int floor, int n)
{
if (floor % 2 == 0 || floor == 1)
{
InsideUp[floor] += n;
}
}

void E3::SetInsideDown(int floor, int n)
{
if (floor % 2 == 0 || floor == 1)
{
InsideDown[floor] += n;
}
}

void E4::SetUp(int floor, int n)
{
if (floor % 2 == 1)
{
Up[floor] += n;
}
}

void E4::SetDown(int floor, int n)
{
if (floor % 2 == 1)
{
Down[floor] += n;
}
}

void E4::SetInsideUp(int floor, int n)
{
if (floor % 2 == 1)
{
InsideUp[floor] += n;
}
}

void E4::SetInsideDown(int floor, int n)
{
if (floor % 2 == 1)
{
InsideDown[floor] += n;
}
}



乘客类

Passenger.h

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
#pragma once
#include <stdlib.h>
#include "Time.h"
#include <Windows.h>
#include "ElevatorLift.h"
#include <time.h>
#include "Group.h"
class Passenger
{

public:
Passenger();
Passenger(int M, int L, ElevatorLift * E[], Time *tm); //构造函数
~Passenger();
void action(); //乘客的活动
void Select(int &group, int &which); //决策选乘的电梯组
char * GetStatus(); //get放法
int getstatus();
int GetNextFloor();
int GetNowFloor();
int Getwhich();
int Gettimes();
int Getgroup();
int GetTotal();
Time NextTime; //下一次乘梯时间
private:
enum Status
{
TAKE, // 乘坐
LEAVE, // 离开
WAIT, // 等待
OVER // 停止乘梯活动
};
Status status; //乘客状态
int WaitTime; //乘客初次等待时间
int NowFloor; //当前楼层
int NextFloor; //目标楼层
int which; //乘坐的电梯
int group; //选定的组号
long Total; //总计等待时间 (单位:秒)
char str[10]; //存放电梯状态的字符串
Time *nowtime; //当前时间指针
ElevatorLift * e[10]; //电梯的指针
Group * g[5]; //电梯组的指针
unsigned int times; //总乘梯次数


};

Passenger.cpp

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
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
//#include "pch.h"
#include "Passenger.h"

Passenger::Passenger()
{
}

Passenger::Passenger(int M, int L, ElevatorLift * E[], Time * tm)
{
NextFloor = (rand() % (40 - 1)) + 1 + 1; //随机生成目标楼层
status = LEAVE; //初始状态为离开状态
NowFloor = 1; //初始楼层为1
WaitTime = (rand() % (M * 60 - 1)) + 1; //乘客初次等待时间
Total = 0; //总计等待时间
times = L + 1; //总乘梯次数
nowtime = tm;
NextTime = (*nowtime + WaitTime); //设定下一次乘梯的时间
for (int i = 0; i < 10; i++)
{
e[i] = E[i];
}
Select(group, which);
}


Passenger::~Passenger()
{
}

void Passenger::action()
{

switch (status)
{

case WAIT:
//当前状态为等待,则查看电梯是否到达当前楼层,将状态置为 “乘坐”
if (e[which]->people < e[which]->capacity)
{ //如果电梯人数未满
if (e[which]->NowFloor == NowFloor)
{ //选定的电梯楼层与乘客所在楼层一致
if (((NextFloor - NowFloor) > 0 && (e[which]->NextFloor - e[which]->NowFloor) >= 0) ||
((NextFloor - NowFloor) < 0 && (e[which]->NextFloor - e[which]->NowFloor) <= 0))
{ //并且方向一致
status = TAKE;
//统计等待
Total = Total + (*nowtime - NextTime);
//设定乘坐电梯的目标楼层
if ((NextFloor - NowFloor) > 0)
{
e[which]->SetInsideUp(NextFloor, 1); //设置目标楼层
e[which]->SetUp(NowFloor, -1); //撤销呼叫
e[which]->people++;
}
else if ((NextFloor - NowFloor) < 0)
{
e[which]->SetInsideDown(NextFloor, 1); //设置目标楼层
e[which]->SetDown(NowFloor, -1); //撤销呼叫
e[which]->people++;
}
else
{

}
}
}
}
if ((*nowtime - NextTime) % 60 == 0)
{
//如果等待时间每超过一分钟 重新选择电梯
//首先撤销上次的选择记录
int old_which = which;
if ((NextFloor - NowFloor) > 0)
{

e[old_which]->SetUp(NowFloor, -1);
}
else
{
e[old_which]->SetDown(NowFloor, -1); ;
}
Select(group, which); //如果电梯满员 重新选择电梯
//重新设置新的电梯记录
if ((NextFloor - NowFloor) > 0)
{

e[which]->SetUp(NowFloor, 1); //Up[this->NowFloor] += 1;
}
else
{
e[which]->SetDown(NowFloor, 1); //Down[this->NowFloor] += 1;
}
}

break;
case TAKE:
NowFloor = e[which]->NowFloor; //更新当前乘坐电梯的楼层
//当前状态为乘坐,则查看是否到达目的楼层 ,将状态置为 “离开”或“停止”
if (NowFloor == NextFloor)
{
times--;
switch (times)
{
case 0:
status = OVER;
break;
case 1:
status = LEAVE;
NextFloor = 1;
NextTime = (*nowtime + 10);
break;
default:
status = LEAVE;
//设定新的目标楼层
int temp = ((rand() % 40) + 1);
while (temp == NowFloor) //如果取出的数是当前楼层,则重新取随机数,直到不同
{
temp = ((rand() % 40) + 1);
}
NextFloor = temp;
NextTime = (*nowtime + ((rand() % 111) + 10)); //生成下一次乘梯时间,随机停留10-120秒
break;
}
e[which]->people--;

}
break;
case LEAVE:
//当前状态为离开,则查看是否到下次开始乘梯的时间 ,将状态置为 “等待”

if (nowtime->second == NextTime.second && nowtime->minute == NextTime.minute &&nowtime->hour == NextTime.hour)
{
//改变状态
status = WAIT;
//选择乘坐的电梯组,与电梯
Select(group, which);
//设置电梯停靠楼层
if ((NextFloor - NowFloor) > 0)
{

e[which]->SetUp(NowFloor, 1); //Up[this->NowFloor] += 1;
}
else
{
e[which]->SetDown(NowFloor, 1); //Down[this->NowFloor] += 1;
}

}
break;
case OVER:
break;
}

}
char * Passenger::GetStatus()
{
switch (status)
{

case WAIT:
strcpy_s(str, "等待");
break;
case TAKE:
strcpy_s(str, "乘坐");
break;
case LEAVE:
strcpy_s(str, "离开");
break;
case OVER:
strcpy_s(str, "结束");
break;
}
return str;
}

int Passenger::getstatus()
{
return status;
}

int Passenger::GetNextFloor()
{
return NextFloor;
}

int Passenger::GetNowFloor()
{
return NowFloor;
}

int Passenger::Getwhich()
{
return which + 1;
}

int Passenger::Gettimes()
{
return times;
}

int Passenger::Getgroup()
{
return group + 1;
}

int Passenger::GetTotal()
{
return Total;
}

void Passenger::Select(int & group, int & which)
{
int select[10]; //设定决策权重数组
for (int i = 0; i < 10; i++) //初始化
{
select[i] = 0;
}
select[0] = 1;
select[1] = 1;
//如果当前楼层与目标楼层为1,25-40
if (((25 <= NowFloor && NowFloor <= 40) || NowFloor == 1) && ((25 <= NextFloor && NextFloor <= 40) || NextFloor == 1))
{
select[2] += 1;
select[3] = 1;
}
//如果当前楼层与目标楼层为1-25
if ((1 <= NowFloor && NowFloor <= 25) && (1 <= NextFloor && NextFloor <= 25))
{
select[4] += 1;
select[5] = 1;
}
//如果当前楼层与目标楼层为偶数
if ((NowFloor % 2 == 0 || NowFloor == 1) && (NextFloor % 2 == 0 || NextFloor == 1))
{
select[6] += 1;
select[7] = 1;
}
//如果当前楼层与目标楼层为奇数
if ((NowFloor % 2 == 1) && (NextFloor % 2 == 1))
{
select[8] += 1;
select[9] = 1;
}
for (int i = 0; i < 10; i++)
{
if (e[i]->people >= (e[i]->capacity)*0.8)
{
select[i] = 0;
}
}

// 进一步选择
int direction = NextFloor - NowFloor; //设定乘梯方向 >0 向上 <0向下
for (int i = 0; i < 10; i++)
{
//在满足条件的电梯组中选择
if (select[i])
{
//如果乘客要去上面
if (direction > 0)
{
//如果电梯停
if (e[i]->NextFloor - e[i]->NowFloor == 0)
{
select[i] = select[i] + 40 - abs((NowFloor - e[i]->NowFloor)); //距离越近,权重越大
}
//如果电梯向上
else if (e[i]->NextFloor - e[i]->NowFloor > 0)
{
//并且电梯位置低于乘客位置
if (e[i]->NowFloor <= NowFloor)
{
select[i] = select[i] + 40 - abs((NowFloor - e[i]->NowFloor)); //距离越近,权重越大
}
else //电梯位置高于乘客
{
select[i] = select[i] - abs((NowFloor - e[i]->NowFloor));
}
}
//如果电梯向下
else
{
//并且电梯位置高于乘客位置
if (e[i]->NowFloor >= NowFloor)
{
select[i] = select[i] - abs((NowFloor - e[i]->NowFloor)); //距离越近,权重越大
}
else //电梯位置低于乘客
{
select[i] = select[i] + abs((NowFloor - e[i]->NowFloor));
}
}
}
else //如果乘客要去下面
{
//如果电梯停
if (e[i]->NextFloor - e[i]->NowFloor == 0)
{
select[i] = select[i] + 40 - abs((NowFloor - e[i]->NowFloor)); //距离越近,权重越大
}
//如果电梯向上
else if (e[i]->NextFloor - e[i]->NowFloor > 0)
{
//并且电梯位置低于乘客位置
if (e[i]->NowFloor <= NowFloor)
{
select[i] = select[i] - abs((NowFloor - e[i]->NowFloor)); //距离越近,权重越大
}
else //电梯位置高于乘客
{
select[i] = select[i] + abs((NowFloor - e[i]->NowFloor));
}
}
//如果电梯向下
else
{
//并且电梯位置高于乘客位置
if (e[i]->NowFloor >= NowFloor)
{
select[i] = select[i] + 40 - abs((NowFloor - e[i]->NowFloor)); //距离越近,权重越大
}
else //电梯位置低于乘客
{
select[i] = select[i] - abs((NowFloor - e[i]->NowFloor));
}
}

}
}
}

//在select数组中取最大值的下标,即为最优选择电梯组
int max = 10;
for (int i = 9; i >= 0; i--)
{
if (select[i] != 0 && (select[max] < select[i]))
{
max = i;
}
}
which = max;
group = (max / 2);

}


分组类

Group.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#pragma once
#include "ElevatorLift.h"

class Group
{
public:
Group(ElevatorLift *e1, ElevatorLift *e2);
~Group();
void action();
private:
ElevatorLift *e1;
ElevatorLift *e2;
friend class Passenger;
};

Group.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "Group.h"
Group::Group(ElevatorLift * e1, ElevatorLift * e2)
{
this->e1 = e1;
this->e2 = e2;
}
Group::~Group()
{
}

void Group::action()
{
e1->action();
e2->action();
}

时间类

Time.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#pragma once
class Time
{
public:
unsigned int hour;
unsigned int minute;
unsigned int second;
Time();
Time( int hour, int minute, int second );
Time operator + (int sec); //时间自加,单位为秒
long operator - (Time & sub);
void set( int hour,int minute, int second);
//void operator =(Time &time);
~Time();
};

Time.cpp

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
//#include "pch.h"
#include "Time.h"


Time::Time()
{
this->hour = 0;
this->minute = 0;
this->second = 0;
}

Time::Time(int hour, int minute, int second)
{
this->hour = hour;
this->minute = minute;
this->second = second;
}



Time Time::operator+ ( int sec)
{
Time time;
time.hour = this->hour;
time.minute = this->minute ;
time.second=this->second;
if (sec < 0)
return time;
time.second = time.second + sec;
if (time.second >= 60)
{
time.minute = time.minute + time.second / 60;
time.second = time.second % 60;
if (time.minute >= 60)
{
time.hour = time.hour + time.minute / 60;
time.minute = time.minute % 60;
if (time.hour >= 24)
{
time.hour = time.hour - 24;
}
}
}

return time;
}

long Time::operator-(Time & sub)
{
long a; //被减数
long b; //减数
a = this->hour * 3600 + this->minute * 60 + this->second;
b = sub.hour * 3600 + sub.minute * 60 + sub.second;
return a - b;
}

void Time::set( int hour, int minute, int second)
{
this->hour = hour;
this->minute = minute;
this->second = second;
}


Time::~Time()
{
}

Extra

最后来扯些别的。

电梯群控调度是一类开放、动态、复杂系统的多目标优化问题。而电梯调度算法是一种常见的算法设计问题。随着楼层的增加,电梯数的增多,这方面研究也非常多。

值得注意的是,这方面也已经有不少人开始做强化学习相关的了,其出发点是,现存算法不具备自学习、自适应的的能力,电梯面临的实际环境是未知的、不确定的。既定算法针对顾客到达模型的实时决策较为刻板与经验化。若模型参数设置有逻辑上的误差或在运行中发生了一些非预期变化,得到的策略会出现极大误差。

而另一方面电梯群组调度问题可以被建模成一个很典型的 MDP问题,即电梯下一时刻状态只取决于当前的电梯状态和调度策略。这样就可以利用强化学习算法自适应学习最优策略,从而实现多路电梯的调度。故可以电梯作为Agent,外界不同楼层乘客按按钮作为Environment,而每个Agent(电梯)即做出选择进行Action,如下:

其核心在于设计环境行为交互的奖励机制,利用DQN强化学习算法,寻找多路电梯各状态下的最优动作序列。

这方面我找到了一篇文章:《Optimal Elevator Group Control via Deep Asynchronous Actor–Critic Learning》,供参考。算法框架如下:

这篇博客到这里就结束了。