一、问题描述:
1、题目内容:使用队列模拟理发馆的排队现象,通过仿真手法评估其营业状况。
设某理发馆设有N把理发椅,可同时为N位顾客进行理发。当顾客进门时,若有空闲理发椅,则立即入座理发,否则依次排队候理,一旦有顾客理完发离去时,排在队头的顾客便开始理发。假若理发馆每天连续营业T小时(只要有顾客等待,理发椅就不空),求一天内顾客在理发馆内的平均逗留时间(包括理发所需时间和排队等候的时间)与顾客排队等候理发的人数的平均值(排队长度的平均值)。
2、基本要求
(1)当给定理发椅数及营业时间后,由随机数确定顾客理发时间及进门间隔时间,求出一天内顾客在理发馆平均逗留时间、平均队长及关门后收尾工作的时间。
(2)由用户读入的数据仅为理发椅数及营业时间。营业的时间以分钟计,理发椅数及关门时间均为整型,且均大于等于1。
3、测试数据
理发椅数目N及关门时间由用户读入,第一个顾客进门的时刻为0,之后每个顾客的进门时刻在前一个顾客进门时设定。即在进门事件发生时随即产生两个随机数(DU,IN),DU为进门顾客理发所需的服务时间(简称理发时间);IN为下一个顾客到达的时间间隔(简称间隔时间)。
二、实现提示:
R为由随机数发生器的随机数,顾客理发时间和顾客之间的间隔时间不妨假设与R有关,可以由下式确定:
DU=15+R%50 IN=2+R%10
1 //2013-5-25 2 //panda 3 4 //定义客户类 5 //Guest.h 6 #pragma once 7 8 #ifndef _GUEST 9 #define _GUEST 10 11 class Guest 12 { 13 public: 14 int id; //客户id 15 int swtime; //记录客户进店的时间 16 int servertime; //服务时间 17 int ncgtime; //下个客户到达时间 18 public: 19 Guest(); //默认构造函数 20 Guest(int id,int swtime,int stime,int ncgtime); 21 Guest(const Guest& g); //复制构造函数 22 Guest& operator=(const Guest& g); //重载=运算符 23 }; 24 25 #endif 26 27 //panda 28 //2013-5-21 29 //Queue.h 30 #pragma once 31 #ifndef _QUEUE 32 #define _QUEUE 33 #include"Guest.h" 34 35 #ifndef _LNODE 36 #define _LNODE 37 //节点结构 38 struct LNode{ 39 Guest data; //值域 40 LNode* next; //连接指针域 41 }; 42 #endif 43 44 class Queue{ 45 private: //数据定义 46 LNode* front; //队首指针 47 LNode* rear; //队尾指针 48 public: //操作 49 Queue(); //初始化队列 50 void InitQueue(); //初始化队列,置为空 51 void EnQueue(LNode item); //将新元素插入到队尾 52 LNode OutQueue(); //从队列中删除队首元素并返回 53 LNode PeekQueue(); //返回队列元素,但是不删除 54 bool EmptyQueue(); //判断队列是否为空 55 void ClearQueue(); //清除队列,使之为空 56 int GetQueueLength(); //获得队列的长度 57 //debug 58 //void PrintQueue(); //输出队列 59 }; 60 #endif 61 62 //panda 63 //2013-5-23 64 //BarBerShop.h 65 #pragma once 66 #ifndef _BARBERSHOP 67 #define _BARBERSHOP 68 #include"Queue.h" 69 //barbershop 定义理发店类 70 71 class BarBerShop 72 { 73 private: 74 // 店铺属性 75 int chair; //n把发椅 76 Queue* chairqueue; //n把发椅的相应的队列 77 //int startime; //开始营业时间 78 int workhour; //营业时间长度 79 int workingtime; //营业时间,是分为单位 80 static int guestid; //客户id分配 81 //问题 82 float atime; //客人平均逗留时间,等待时间和理发时间 把每个客人的逗留时间加起来,等停止营业的时候在除于中的客人总数 83 int samplingtimes; //采样次数,即循环次数 84 float aqlength; //平均队列长度 把每次循环的平均队长再除以中的循环次数 85 int cworktime; //关门后收尾工作时间 86 //状态变量 87 int ngct; //下个客人到达时间,既调用produceguest函数的时间点 88 int* stq; //每条队列的队首客户的服务剩余时间 89 int ctime; //运行中的时间点 90 int cuttime; //下次运行的时间片 91 //config setting 92 static const int stime=15; //服务时间常量 93 static const int sftime=50; //服务时间因子 94 static const int ngtime=2; //下一个客人到达时间常量 95 static const int ngftime=10; //下一个各人到达时间因子 96 public: 97 BarBerShop(); //默认构造函数 98 void RunWorking(); //接口,封装了整个营业流程 99 void PrintData(); //输出统计数据 100 protected: 101 void PrepareWork(); //开门前的准备工作,如打扫卫生,清理桌面 102 void StartWork(); //开始营业挣钱娶老婆 103 void CloseDoor(); //关门和老婆吃饭散步去 104 int DistributeGuest(Guest& g,int time); //分发客户,原则是那里人少添加到那个队列里,并返回是第几队队列(虽然理论上,那个队列的人所用的服务时间最少应该添加到那个队列里,但是在真正的生活中,一般都是排在人少的那条队列后面) 105 Guest ProduceGuest(); //返回客户id,和下个客户到达时间,和该客户到达时间 106 void QueueWork(Queue queue); //每条队列的处理函数 107 private://辅助函数 108 //int MinNumber(int* array,int ngct,int alength); //求最小值 109 int MinQueue(Queue* q,int length); //求队列中人最少的一条 110 int GetCutTime(int ngct,int* stq,int l); //ngct下个客户到达时间,stq客户服务剩余时间,l为长度 获取本轮循环的减少时间,返回值表示下次循环是那个对象的执行时间到了 -1表示下个客户到了 111 bool EmptyQueueChair(Queue* q,int l); //判断队列数组是否全为空 112 float GetAverageQueueLength(Queue* q,int l); //获取队列的平均长度 113 }; 114 #endif 115 116 //panda 117 //2013-5-25 118 //Guest.cpp 119 #include"Guest.h" 120 // 121 //默认构造函数 122 // 123 Guest::Guest() 124 {} 125 // 126 // 127 // 128 Guest::Guest(int id,int swtime,int stime,int ncgtime) 129 { 130 this->id=id; 131 this->swtime=swtime; 132 this->servertime=stime; 133 this->ncgtime=ncgtime; 134 } 135 // 136 //复制构造函数 137 // 138 Guest::Guest(const Guest& g) 139 { 140 id=g.id; 141 swtime=g.swtime; 142 servertime=g.servertime; 143 ncgtime=g.ncgtime; 144 } 145 // 146 //重载= 147 // 148 Guest& Guest::operator=(const Guest& g) 149 { 150 id=g.id; 151 swtime=g.swtime; 152 servertime=g.servertime; 153 ncgtime=g.ncgtime; 154 return *this; 155 } 156 157 //2013-5-21 158 //panda 159 //Queue.cpp 160 #include<iostream> 161 #include"Queue.h" 162 using namespace std; 163 164 //Queue成员函数实现 165 166 // 167 //初始化队列,置为空 168 // 169 void Queue::InitQueue() 170 { 171 front=rear=NULL; 172 } 173 // 174 //将新元素插入到队尾 175 // 176 void Queue::EnQueue(LNode item) 177 { 178 //创建待插入的节点,并申请内存空间 179 LNode* newnode=new LNode(); 180 //填充数据 181 newnode->data=item.data; 182 newnode->next=NULL; 183 //插入到队尾 184 if(rear==NULL) 185 { 186 //如果链表为空,则新的结点既是队首又是队尾 187 front=rear=newnode; 188 }else{ 189 //如链表非空,则新结点被连接到队尾并修改队尾指针 190 rear=rear->next=newnode; 191 } 192 } 193 // 194 //从队列中删除队首元素并返回 195 // 196 LNode Queue::OutQueue() 197 { 198 //如果链队为空,则返回 199 if(front==NULL) 200 { 201 cerr<<"链队为空,无法删除"<<endl; 202 exit(1); 203 } 204 //储存待返回内容 205 LNode node; 206 node.data=front->data; 207 node.next=NULL; 208 //队首指针前移 209 LNode* delnode=front; 210 front=front->next; 211 delete delnode; 212 //若删除后链队为空,则使队尾指针为空 213 if(front==NULL) 214 { 215 rear=NULL; 216 } 217 return node; 218 } 219 // 220 //返回队列元素,但是不删除 221 // 222 LNode Queue::PeekQueue() 223 { 224 if(front==NULL) 225 { 226 cerr<<"链队为空,无队首元素。"<<endl; 227 exit(1); 228 } 229 //返回队首元素 230 LNode node; 231 node.data=front->data; 232 node.next=NULL; 233 return node; 234 } 235 // 236 //判断队列是否为空 237 // 238 bool Queue::EmptyQueue() 239 { 240 return front==NULL; 241 } 242 // 243 //清除队列,使之为空 244 // 245 void Queue::ClearQueue() 246 { 247 //队首指针赋给p 248 LNode* p=front; 249 //依次删除队列中的每个结点 250 while (p!=NULL) 251 { 252 front=front->next; 253 delete p; 254 p=front; 255 }//循环结束后队首指针已经为空 256 //置队尾指针为空 257 rear=NULL; 258 } 259 // 260 //debug 输出队列 261 // 262 //void Queue::PrintQueue() 263 //{ 264 // if (front==NULL) 265 // { 266 // cout<<"队链为空。"<<endl; 267 // return ; 268 // } 269 // LNode *p=front; 270 // while (p!=NULL) 271 // { 272 // cout<<p->data<<" "; 273 // p=p->next; 274 // } 275 //} 276 // 277 //初始化队列 278 // 279 Queue::Queue() 280 { 281 front=rear=NULL; 282 } 283 // 284 //获得队列的元素的长度 285 //ok 286 int Queue::GetQueueLength() 287 { 288 int length=0;//存储队列长度 289 LNode *p=front; 290 while (p!=NULL) 291 { 292 ++length; 293 p=p->next; 294 } 295 return length; 296 } 297 298 //panda 299 //2013-5-24 300 301 //BarBerShop类的实现 302 //BarBerShop.cpp 303 #include"BarBerShop.h" 304 #include"Guest.h" 305 306 #include<iostream> 307 using namespace std; 308 309 //初始化静态成员变量 310 int BarBerShop::guestid=0; 311 // 312 //准备工作,读入数据 呵呵~ 313 //ok 314 void BarBerShop::PrepareWork() 315 { 316 cout<<"请输入理发店椅子的数目:"; 317 cin>>chair; 318 /*if(chair<=0) 319 { 320 cout<<"输入数据有误,请重新输入!"<<endl; 321 cin>>chair; 322 }*/ 323 //为每张椅子生成一条队列 324 chairqueue=new Queue[chair]; 325 stq=new int[chair]; 326 cout<<"请输入营业时间(以小时为单位):"; 327 cin>>workhour; 328 /*if(workhour<8) 329 { 330 cout<<"请不要偷懒,8个小时都不够,什么时候才能挣够钱娶老婆啊?!亲~,请重新输入"<<endl; 331 cin>>workhour; 332 }else if (workhour>24) 333 { 334 cout<<"亲~,一天才25个小时,你要做多久啊?!会累死人的,请重新输入数据"<<endl; 335 cin>>workhour; 336 }*/ 337 workingtime=workhour*60; //化为分钟 338 cout<<"营业时间长度为"<<workingtime<<"分钟。"<<endl; 339 } 340 // 341 //分发客户 342 //ok 343 int BarBerShop::DistributeGuest(Guest& guest,int time) 344 { 345 //找出队列中最少人的那一条 346 int i=MinQueue(chairqueue,chair); 347 LNode node; 348 node.data=guest; 349 node.next=NULL; 350 //把结点放在相应的队列 351 chairqueue[i].EnQueue(node); 352 cout<<"在"<<time<<"时刻,id为"<<guest.id<<"的客户进到店,并排在第"<<i<<"条队列里。"<<endl; 353 return i; 354 } 355 // 356 //生产客户 357 //ok 358 Guest BarBerShop::ProduceGuest() 359 { 360 Guest nguest; 361 //填充guest对象 362 nguest.id=guestid++; 363 nguest.ncgtime=ngtime+rand()%ngftime; 364 nguest.servertime=stime+rand()%sftime; 365 return nguest; 366 } 367 // 368 //开始营业 369 // 370 //通过while循环,递减workingtime变量来模拟时间运动, 371 //并通过各个变量来记得下次该函数的调用时间 372 //来模拟营业的流程 373 void BarBerShop::StartWork() 374 { 375 //开始营业 376 cout<<"在0点"<<"开始营业,欢迎光临!(方便数据的调试,以0点开始营业)"<<endl; 377 //int ngct=0; //下个客户到达时间,即调用produceGuest函数的时间 378 //int* stq=new int[chair]; //每条队列的客户服务剩余时间 379 //清空为0 380 for (int i = 0; i < chair; i++) 381 { 382 stq[i]=0; 383 } 384 //int cuttime=0; //每次循环将要减少的时间 385 //int ctime=0; //当前时间 386 //int workprogress=workingtime; //工作进度 387 while (ctime<workingtime) 388 { 389 //开始工作 390 //相当于执行一轮 391 cuttime=GetCutTime(ngct,stq,chair); 392 //每循环一次,各个时间片都减去本次的循环时间片 393 //workprogress-=cuttime; 394 //判断cuttime是否超出范围 395 if ((ctime+cuttime)>workingtime) 396 { 397 cuttime=workingtime-ctime; 398 } 399 ctime+=cuttime; 400 ngct-=cuttime; 401 for (int i = 0; i < chair; i++) 402 { 403 if (stq[i]!=0) 404 { 405 stq[i]-=cuttime; 406 } 407 408 } 409 //具体执行的内容 410 //客户部分 411 if (ngct==0&&ctime!=workingtime) //如果下个客户到达的时间到了,且没到关门时间,那就生产一个客户,并投递到相应的队列里 412 { 413 Guest g=ProduceGuest(); 414 //stq只记录队列首位客户的服务时间 415 g.swtime=ctime; //记录进店时间 416 int qindex=DistributeGuest(g,ctime); 417 if (stq[qindex]==0) 418 { 419 stq[qindex]=g.servertime; 420 } 421 //stq[DistributeGuest(g,ctime)]=g.servertime; //分发客户,并记录服务时间 422 ngct=g.ncgtime; //记录下个客户到达时间 423 } 424 //理发队列 425 for (int i = 0; i < chair; i++) 426 { 427 //队列里有客户在等,且执行时间到了 428 if (stq[i]==0&&chairqueue[i].GetQueueLength()!=0) 429 { 430 //弹出队首元素 431 LNode opnode=chairqueue[i].OutQueue(); 432 //记录该客户的等待时间 433 atime+=(ctime-opnode.data.swtime); 434 //更新stq变量 435 if (!chairqueue[i].EmptyQueue()) 436 { 437 stq[i]=chairqueue[i].PeekQueue().data.servertime; 438 } 439 440 cout<<"在"<<ctime<<"时刻,第"<<i<<"条队列的id为"<<opnode.data.id<<"的客户理完发。"<<endl; 441 } 442 } 443 //获取队列的平均长度 444 aqlength+=GetAverageQueueLength(chairqueue,chair); 445 //统计函数调用次数 446 ++samplingtimes; 447 } 448 cout<<"下班时间到了,不接客了,把剩下的客人剪完就吃饭去,好饿啊。"<<endl; 449 } 450 // 451 //求最小值 452 // 453 //int BarBerShop::MinNumber(int* array,int ngct,int alength) 454 //{ 455 // int min=array[0]; 456 // //找出数组最小的值 457 // for (int i = 1; i < alength; i++) 458 // { 459 // if (min<array[i]) 460 // { 461 // min=array[i]; 462 // } 463 // } 464 // return (min>ngct)?ngct:min; 465 //} 466 // 467 //求最少人的队列 468 //ok 469 int BarBerShop::MinQueue(Queue* q,int l) 470 { 471 int index=0; //最少人队列下标 472 int l0=q[0].GetQueueLength(); 473 int li; //队列长度 474 for (int i = 1; i < l; i++) 475 { 476 li=q[i].GetQueueLength(); 477 if (l0>li) 478 { 479 l0=li; 480 index=i; 481 } 482 } 483 return index; 484 } 485 // 486 //获取本轮减少时间片,返回下去那个对象的执行时间到了 487 //ok 488 int BarBerShop::GetCutTime(int ngct,int *stq,int l) 489 { 490 //只有那些队列的长度不为0的队列的stq值才有效 491 int min=ngct; 492 //找出数组最小的值 493 for (int i = 0; i < l; i++) 494 { 495 if (chairqueue[i].GetQueueLength()!=0) 496 { 497 if (min>stq[i]) 498 { 499 min=stq[i]; 500 } 501 } 502 503 } 504 return (min>ngct)?ngct:min; 505 } 506 // 507 //开始运作 508 // 509 void BarBerShop::RunWorking() 510 { 511 //准备工作 512 PrepareWork(); 513 //开门迎客 514 StartWork(); 515 //关门吃饭 516 CloseDoor(); 517 } 518 // 519 //构造函数 520 //ok 521 BarBerShop::BarBerShop() 522 { 523 chair=0; 524 chairqueue=NULL; 525 workhour=0; 526 workingtime=0; 527 atime=0; 528 aqlength=0; 529 cworktime=0; 530 ngct=0; 531 stq=NULL; 532 ctime=0; 533 cuttime=0; 534 samplingtimes=0; 535 //guestid=0; 536 } 537 // 538 //停止营业,清尾工作 539 //还是觉得把这个清尾工作写成一个函数,尽管和startwork函数有不少相同的地方 540 void BarBerShop::CloseDoor() 541 { 542 int bltime=stime+sftime+100; //只是为了调用GetCutTime函数,无用参数变量 543 while (!EmptyQueueChair(chairqueue,chair)) //只要队列不全为空就一直循环 544 { 545 cuttime=GetCutTime(bltime,stq,chair); //获取循环减少的时间片 546 ctime+=cuttime; //记录时间 547 for (int i = 0; i < chair; i++) 548 { 549 if (stq[i]!=0) 550 { 551 stq[i]-=cuttime; 552 } 553 } 554 //理发队列 555 for (int i = 0; i < chair; i++) 556 { 557 //队列里有客户在等,且执行时间到了 558 if (stq[i]==0&&chairqueue[i].GetQueueLength()!=0) 559 { 560 //弹出队首元素 561 LNode opnode=chairqueue[i].OutQueue(); 562 //记录客户的等待时间 563 atime+=(ctime-opnode.data.swtime); 564 //更新stq变量 565 if (!chairqueue[i].EmptyQueue()) 566 { 567 stq[i]=chairqueue[i].PeekQueue().data.servertime; 568 } 569 570 cout<<"在"<<ctime<<"时刻,第"<<i<<"条队列的id为"<<opnode.data.id<<"的客户理完发。"<<endl; 571 } 572 } 573 //获取队列的平均长度 574 aqlength+=GetAverageQueueLength(chairqueue,chair); 575 //统计函数调用次数 576 ++samplingtimes; 577 } 578 cout<<"终于弄好了,下班吃饭。今晚吃什么菜呢?!亲~"<<endl; 579 } 580 // 581 //判断队列数组是否全为空 582 // 583 bool BarBerShop::EmptyQueueChair(Queue* q,int l) 584 { 585 for (int i = 0; i < l; i++) 586 { 587 if (false==q[i].EmptyQueue()) 588 { 589 // 不为空 590 return false; 591 } 592 } 593 //为空 594 return true; 595 } 596 // 597 //获取队列的平均长度 598 // 599 float BarBerShop::GetAverageQueueLength(Queue* q,int l) 600 { 601 int averagelength=0; 602 for (int i = 0; i < l; i++) 603 { 604 averagelength+=q[i].GetQueueLength(); 605 } 606 return (averagelength/(float)l); 607 } 608 // 609 //输出统计数据 610 // 611 void BarBerShop::PrintData() 612 { 613 cout<<"关门后的收尾工作时间为:"<<ctime-workingtime<<endl 614 <<"顾客的平均逗留时间为:"<<(atime/(float)guestid)<<endl 615 <<"排队队列的平均长度:"<<(aqlength/(float)samplingtimes)<<endl; 616 } 617 618 //panda 619 //2013-5-21 620 #include<iostream> 621 #include"BarBerShop.h" 622 623 using namespace std; 624 625 int main() 626 { 627 BarBerShop bbshop; 628 bbshop.RunWorking(); 629 bbshop.PrintData(); 630 return 0; 631 }
时间: 2024-10-12 01:54:37