操作系统—电梯调度模拟程序(C语言,数据结构,含代码)
各位好,这里是太阳终于出来啦,这次分享的是操作系统课程中的电梯模拟程序,虽然个人的观点是这个程序和操作系统的关系不大,倒像是数据结构的练习。所以各位就姑且当数据结构来看吧。
目录
一、写在前面
1.本人并不擅长编程,各位可以交流学习,如果有错误欢迎指出。
2.不保证思路和解决方式是最佳思路,也不能保证正确性,请勿将本文当做考试复习参考。其中涉及到专业名词的部分可能会有描述错误,请谅解。
3.本人个人写代码不习惯写注释,变量的命名也很随意,请谅解。
4.本文会讲述全部代码思路,代码是按模块分段展示的(也会在文中提及主函数的编写逻辑),代码需要自己整合,有编程基础的朋友一定能在阅读后根据提示写出完整程序。因为看到很多朋友在解决此法时遇到困难,仅作为交流。如果是想要直接复制完整代码的本站有其他大佬的分享帖。
5.请谅解文章中的错别字,标点符号,以及的地得!
6.ncwu慎用。
二、题目要求
电梯的模拟程序
基本条件如下:
楼层:1-21
电梯容量:10个人
电梯每0.5s经过一个楼层
初始一楼有30人等电梯并且每5s会来0-2个人
每5s输出一次电梯一共走过的层数,电梯目前所在的层数,一楼所等待的人数
三、思路分析
这里,我的想法是为每一个乘客创建一个结构体,然后每一层的等待乘客是一个链表,电梯内的乘客自己也是一个链表,每有一个乘客上电梯,就把楼层链表中的相应乘客移动到电梯的列表中,而每有一个乘客下电梯就把电梯链表中的相应元素删除。
这里其实题目中只要求了一楼会每隔5秒来人,但是这里想优化一下就是,凡是有人下电梯(不是一楼)那么这个楼层也会来人(当然这里也是随机,且来的人数不能大于下电梯在这里楼的人数,虽然现实中会存在爬楼梯什么的不用考虑这么复杂,我们就先当这个大楼没有楼梯来着,主要是程序编好了才想到有楼梯这回事……)
具体说明如下:
typedef struct LNode //每个乘客一个链表项
{
int from;
int to;
int uod;//上行还是下行 ,1表示上行
struct LNode *next;
}LNode,*LinkList;
这里方便管理用的是链表而不是顺序表。from是每个乘客出发的楼层,to是每个乘客到达的楼层,uod是up_or_down,记录电梯是上行还是下行,因为电梯是有点像磁盘的LOOK调度,他要一直同一个方向送到(当前乘客或有人等待的楼层)最大值(或最小值)然后再转向。所以只有电梯运行方向和乘客运行方向一致时,乘客才会上电梯。
这里一共创建了三个函数,分别是链表添加,链表转移和链表删除。
void createlinklist(LinkList &L,int n,int f);
int addlinklist(LinkList &L,LinkList &LL,int a,int b);//a表示电梯状态
void movelinklist(LinkList &L,int a);//a表楼层
这里基本与链表操作一致,只说明适配这个程序的不同之处,这里的链表初始化我放在了主程序里,因为本身链表就不想多建一个函数,而在链表的添加函数中,开始需要移动指针到最后然后用后插法插入新的节点,要注意的有两点,一是生成的随机数不能与当前楼层相同,二是要判断上行还是下行。
完整代码如下:
void createlinklist(LinkList &L,int n,int f){//n表示链表生成数量,f表示楼层
LNode *p=NULL, *r=NULL;
int i,t;
r=L;
while(r->next) r=r->next;
for(int i=1;i<=n;i++){//创建n个链表,每个链表就代表一个乘客
p=new LNode;
p->from=f;
t=random(1,21);
while(t==f) t=random(1,21);
p->to=t;
if(f<t) p->uod=1;
if(f>t) p->uod=0;
p->next=NULL;
r->next=p;
r=p;
}
}
Random为在程序开头的宏定义
#define random(a,b) (rand()%(b-a+1)+a)
链表的转移函数中,我设置了返回值,因为不一定每个楼层都有人上电梯(这个楼层可能有人在等电梯,但是可能运行方向不一致)返回值就是用来判断有没有人上电梯,因为如果没有这个判断条件仅仅是以电梯满了没或者是等待的人消失了没作为判断循环结束的条件的话就会出现死循环。代码如下:
int addlinklist(LinkList &L,LinkList &LL,int a,int b){ //a表示上行or下行
LNode *p=NULL,*p1=NULL,*r=NULL;
r=L;
int i=0,flag;
while(r && (i<b-1)) {//先把指针移到表尾
r=r->next;
i++;
}
p1=LL->next;
flag=1;
if(p1->uod==a){
p=new LNode;
p->from=p1->from;
p->to=p1->to;
p->uod=p1->uod;
p->next=r->next;
r->next=p;
p1=p1->next;
LL->next=p1;
flag=0;
return 1;
}
if(flag==1) return 0;
}
要注意的是,如果有人要上电梯,就要把该层的等待链表的相对应的节点删除(移动头指针就可以了,如下图画圈部分)。
而删除部分只要注意相应的楼层数组标记值就行了(为了这一层到时候可以出现等电梯的人,开头说的)代码如下:
void movelinklist(LinkList &L,int a){//a表示楼层
LNode *p=NULL,*q=NULL;
p=L;
for(int i=1;i<=amount;i++){
if(p->next->to==a){
q=p->next;
p->next=q->next;
amount--;//电梯人数减一
FF[a]++;//楼层人数加一
i--;
}
else p=p->next;
}
}
这里解释一下为什么p=p->next要放到else部分执行(顺便复习一下链表的删除),假设有前后两个元素值一样的情况,如图,假设我们执行p=p->next,那么则如图所示。
此时由于我们的判断条件是p->next->to==a,所以第二个6被我们跳过了,所以执行删除时候不能往下遍历,而至于为什么i要--,这里也很简单,用下图说明。
这里假设我们要删除的元素是2,那么因为i的循环长度是链表的长度,所以最后一个元素没办法遍历到啦。
这里还有一个函数check用来判断当前电梯最高和最低运行的层数,也就是一个很简单的遍历并寻找最大值最小值的函数。这里前面解释过了电梯不会一直都是在21和1层之间来回运动,他只会运动到当前(等待)电梯里的人要去往的最大(最小)楼层。
在check函数中先将初始值定为10.5,因为没有楼层是10.5,这样如果函数结束两个变量的值还是10.5,就说明没有发生变化,我们就把他们的值变为1,让电梯回到一楼等待。这个函数用的是最简单的比大小找最大值最小值法,就不过多阐述,代码如下:
void check(LinkList& L) {
themin = 10.5;
themax = 10.5;
for (int j = 1; j <= 21; j++) {//比较有人等电梯楼层的极值
if (Floor[j] > 0 && j > themax) themax = j;
if (Floor[j] > 0 && j < themin) themin = j;
}
if(amount==0) return;
LNode* p=NULL;
p = L->next;
while (p) {//遍历然后得到电梯里的人目的地的极值
if (p->to > themax) themax = p->to;
if (p->to < themin) themin = p->to;
p = p->next;
}
if (themin == 10.5) themin = 1;//没有变化,归位
if (themax == 10.5) themax = 1;
}
最后是一个每隔五秒来人的函数addpeople,这个很简单,随机出值,然后调用列表的添加就可以了,如果随机出的楼层有人就生成在随机的楼层,如果没人就生成在一楼。代码如下:
void addpeople(){
int n,f;
n=random(0,2); //随机0-2人
for(int i=1;i<=n;i++){
f=random(2,21);//随机一个楼层
if(FF[f]>0){//之前有人到过这个楼层,就生成到这里
createlinklist(L[f],1,f);//楼层链表增加一项
Floor[f]++;//楼层等待人数加一
FF[f]--; //楼层人数减一
}
else {//如果不是就在一楼生成
createlinklist(L[1],1,1);
Floor[1]++;
FF[f]--;
}
}
}
四、主函数及变量说明
为了避免函数参数过多,我建立了很多全局变量,下面是对应的解释:
#define TIME 40//电梯运行时间
int themax,themin;//电梯运行楼层的最大值和最小值
int amount=0,F;//F是当前层数 amount是电梯当前人数
int Floor[22]={0},FF[22]={0};//每一层的人数,以及这层有没有人在等电梯
LinkList L[22];//楼层链表,表示当前楼层的等待的乘客
主函数说明(详见代码注释):
int main(){
srand((unsigned int)time(NULL));//随机数种子
for(int i=1;i<=21;i++){ //链表初始化
L[i]=new LNode;
L[i]->next=NULL;
}
createlinklist(L[1],30,1);//创建一个一楼30个人的等候链表
LNode *p;
LinkList EL;//电梯链表,表示当前电梯中的乘客
void nextfloor(int ud);//表示电梯下一个要到达的楼层,代码见下面
int flag;//判断这层楼有没有人下电梯
EL=new LNode;//初始化
EL->next=NULL;
for(int i=1;i<=10;i++){//把一楼等电梯的前十个人送上电梯
flag=addlinklist(EL,L[1],1,i);
amount++;
}
Floor[1]=20; //第一层还有20个人
int ud;//ud表示上行或下行。
ud=1;//先是上行
F=1;//先是在一楼
for(int i=TIME*2-1;i>=0;i--){//0.5要换成double型太麻烦了所以乘个二
check(EL);//确定当前运行的最大和最小楼层
nextfloor(ud);//运行到下一层
if(F==themax) ud=0;//如果到达最高层,换向
if(F==themin) ud=1;//如果到达最低层,换向
movelinklist(EL,F);//下电梯
while(Floor[F]>0){//上电梯
//(如果这一楼有人在等电梯,考虑上行下行不一定有人等就要上)
if(amount<10){//电梯如果有空位
flag=addlinklist(EL,L[F],ud,amount+1);//看看这楼有没有要上电梯的
if(flag==1){//如果有(一个一个来)
amount++;//电梯总人数加一
Floor[F]--;//当前楼层等待人数减一
}
if(flag==0) break;//没有就退出防止死循环
}
else break;//没人等就退出
}
if(i%10==0 && i!=TIME*2) {//每五秒输出依次
addpeople();//每隔5秒来人
cout<<"现在过了"<<(TIME*2-i)/2<<"秒"<<endl;
cout<<"电梯在第"<<F<<"层"<<endl;
for(int j=1;j<=21;j++){
if(Floor[j]>0) cout<<j<<"楼有"<<Floor[j]<<"个人在等电梯"<<endl;
}
cout<<"电梯里有"<<amount<<"个人"<<endl;
cout<<endl;
}
}
if(amount>0){//顺便看看结束时候电梯里还有几个怨种
p=EL->next;
cout<<"电梯里的人想去的楼层是:";
while(p){
cout<<p->to<<" ";
p=p->next;
}
}
}
这里的nextfloor函数如下:
void nextfloor(int ud){//ud表示上行下行,上行加一下行减一
if(ud==1) F++;
if(ud==0) F--;
}
五、结果及说明
输出结果如下:
当然啦,由于我们采用了链表,所以其实输出可以随心所欲一点,比如说我们可以在结束的时候看看每个楼层等待的人都想去那一层。
只要插入这一段代码。
for (int j = 1; j <= 21; j++) {
if (Floor[j] > 0) {
cout << j << "楼的人想去的楼层是:";
p = L[j]->next;
while (p) {
cout << p->to << " ";
p = p->next;
}
cout << endl;
}
}
就可以输出想要的结果了。
以上,谢谢阅读,感谢你的时间。