电梯调度算法...

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了电梯调度算法...相关的知识,希望对你有一定的参考价值。

参考技术A

不管你是在北上广还是在港澳台,甚至三四线城市,凡是有规模的地区,高楼比比皆是。不管是写字楼,还是大型商城,让你最头痛的就是乘电梯,尤其是在赶时间的时候。

每天早上,那些差5分钟就迟到的程序员,在等电梯时,一般会做两件事:

前者可能是写字楼里上班族惯有的精神类疾病,但后者肯定是程序员的职业病。本文对“骂电梯”不给予任何指导性建议。

但说起电梯调度算法,我觉得还是可以给大家科普一下,好为大家在等电梯之余,打发时间而做出一点贡献。

(电梯调度算法可以参考各种硬盘换道算法,下面内容整理自网络)

先来先服务(FCFS-First Come First Serve)算法,是一种随即服务算法,它不仅仅没有对寻找楼层进行优化,也没有实时性的特征,它是一种最简单的电梯调度算法。

它根据乘客请求乘坐电梯的先后次序进行调度。此算法的 优点是公平、简单,且每个乘客的请求都能依次地得到处理,不会出现某一乘客的请求长期得不到满足的情况

这种方法在载荷较轻松的环境下,性能尚可接受,但是在载荷较大的情况下,这种算法的性能就会严重下降,甚至恶化。

人们之所以研究这种在载荷较大的情况下几乎不可用的算法,有两个原因:

最短寻找楼层时间优先(SSTF-Shortest Seek Time First)算法,它注重电梯寻找楼层的优化。最短寻找楼层时间优先算法选择下一个服务对象的原则是 最短寻找楼层的时间。

这样请求队列中距当前能够最先到达的楼层的请求信号就是下一个服务对象。

在重载荷的情况下,最短寻找楼层时间优先算法的平均响应时间较短,但响应时间的方差较大 ,原因是队列中的某些请求可能长时间得不到响应,出现所谓的“ 饿死”现象

扫描算法(SCAN) 是一种按照楼层顺序依次服务请求,它让电梯在最底层和最顶层之间连续往返运行,在运行过程中响应处在于电梯运行方向相同的各楼层上的请求。

它进行寻找楼层的优化,效率比较高,但它是一个 非实时算法 。扫描算法较好地解决了电梯移动的问题,在这个算法中,每个电梯响应乘客请求使乘客获得服务的次序是由其发出请求的乘客的位置与当前电梯位置之间的距离来决定的。

所有的与电梯运行方向相同的乘客的请求在一次电向上运行或向下运行的过程中完成, 免去了电梯频繁的来回移动

扫描算法的平均响应时间比最短寻找楼层时间优先算法长,但是响应时间方差比最短寻找楼层时间优先算法小, 从统计学角度来讲,扫描算法要比最短寻找楼层时间优先算法稳定

LOOK 算法是扫描算法(SCAN)的一种改进。对LOOK算法而言,电梯同样在最底层和最顶层之间运行。

当 LOOK 算法发现电梯所移动的方向上不再有请求时立即改变运行方向 ,而扫描算法则需要移动到最底层或者最顶层时才改变运行方向。

SATF(Shortest Access Time First)算法与 SSTF 算法的思想类似,唯一的区别就是 SATF 算法将 SSTF 算法中的寻找楼层时间改成了访问时间。

这是因为电梯技术发展到今天,寻找楼层的时间已经有了很大地改进, 但是电梯的运行当中等待乘客上梯时间却不是人为可以控制

SATF 算法考虑到了电梯运行过程中乘客上梯时间的影响

最早截止期优先(EDF-Earliest Deadline First)调度算法是最简单的实时电梯调度算法,它的 缺点就是造成电梯任意地寻找楼层,导致极低的电梯吞吐率。

它与 FCFS 调度算法类似,EDF 算法是电梯实时调度算法中最简单的调度算法。 它响应请求队列中时限最早的请求,是其它实时电梯调度算法性能衡量的基准和特例。

SCAN-EDF 算法是 SCAN 算法和 EDF 算法相结合的产物。SCAN-EDF 算法先按照 EDF 算法选择请求列队中哪一个是下一个服务对象,而对于具有相同时限的请求,则按照 SCAN 算法服务每一个请求。它的效率取决于有相同 deadline 的数目,因而效率是有限的。

PI(Priority Inversion)算法将请求队列中的请求分成两个优先级,它首先保证高优先级队列中的请求得到及时响应,再搞优先级队列为空的情况下在相应地优先级队列中的请求。

FD-SCAN(Feasible Deadline SCAN)算法首先从请求队列中找出时限最早、从当前位置开始移动又可以买足其时限要求的请求,作为下一次 SCAN 的方向。

并在电梯所在楼层向该请求信号运行的过程中响应处在与电梯运行方向相同且电梯可以经过的请求信号。

这种算法忽略了用 SCAN 算法相应其它请求的开销,因此并不能确保服务对象时限最终得到满足。

以上两结介绍了几种简单的电梯调度算法。

但是并不是说目前电梯调度只发展到这个层次。目前电梯的控制技术已经进入了电梯群控的时代。

随着微机在电梯系统中的应用和人工智能技术的发展,智能群控技术得以迅速发展起来。

由此,电梯的群控方面陆续发展出了一批新方法,包括:基于专家系统的电梯群控方法、基于模糊逻辑的电梯群控方法、基于遗产算法的电梯群控方法、基于胜景网络的电梯群控方法和基于模糊神经网络的电梯群控方法。

本人设置的电梯的初始状态,是对住宅楼的电梯的设置。

(1)建筑共有21层,其中含有地下一层(地下一层为停车场)。
(2)建筑内部设有两部电梯,编号分别为A梯、B梯。
(3)电梯内部有23个按钮,其中包括开门按钮、关门按钮和楼层按钮,编号为-1,1,2,3,4……20。
(4)电梯外部含有两个按钮,即向上运行按钮和向下运行按钮。建筑顶层与地下一层例外,建筑顶层只设置有向下运行按钮,地下一层只设置有向上运行按钮。
(5)电梯开关门完成时间设定为1秒。电梯到达每层后上下人的时间设定为8秒。电梯从静止开始运行到下一层的时间设置为2秒,而运行中通过一层的时间为1秒。
(6)在凌晨2:00——4:30之间,如若没有请求信号,A梯自动停在14层,B梯自动停在6层。
(7)当电梯下到-1层后,如果没有请求信号,电梯自动回到1层。

每一架电梯都有一个编号,以方便监控与维修。每一架电梯都有一实时监控器,负责监控电梯上下,向电梯升降盒发送启动、制动、加速、减速、开关电梯门的信号。若电梯发生故障,还应向相应的电梯负责人发送求救信号。

电梯内部的楼层按钮:

这样就表示乘客将要去往此层,电梯将开往相应层。当电梯到达该层后,按钮恢复可以使用状态。

电梯内部开门按钮:

如若电梯到了乘客曾经按下的楼层,但是无乘客按开门按钮,电梯将自动在停稳后1秒后自动开门。

电梯内部关门按钮:

电梯外部向上按钮:

电梯外部向下按钮:

你肯能意识到 哪个算法都不是一个最佳方案,只是它确实解决了一定情况的问题 。但是对一个优秀的程序员而言,研究各种算法是无比快乐的。也许你下一次面试,就有关于调度算法的问题。

电梯调度算法模拟

说明:电梯调度算法的基本原则就是如果在电梯运行方向上有人要使用电梯则继续往那个方向运动,如果电梯中的人还没有到达目的地则继续向原方向运动。具体而言,如果电梯现在朝上运动,如果当前楼层的上方和下方都有请求,则先响应所有上方的请求,然后才向下响应下方的请求;如果电梯向下运动,则刚好相反。

题目难度:较难

设计要求:模拟多人在不同楼层同时要求到各自目的地时电梯的响应顺序,要求使用C语言编程,定义合适的数据结构。最后,需要说明设计思想,同时给出能够运行的源程序,并给出对应的程序流程图。

设计提示:可以用一个结构体表示乘电梯的人,其中内容包括人的姓名、起始楼层、目的楼层;建立一个结构体的数组模拟当前所有需要乘电梯的人。把这个结构体数组作为程序的输入,通过对数组中每个人的起始楼层和目的楼层进行分析,确定每个人进出电梯的顺序,并打印输出。

比如: 当前楼层是4,结构体数组中共有3个人,A:7 → 3  B:6→10 C:7→8;

 则输出应该是: 当前楼层为6,B进入

                当前楼层为7,C进入

                当前楼层为8,C出去

                当前楼层为10,B出去

                当前楼层为7,A进入

                当前楼层为3,A出去


解题思路:

      用户结构体:{用户姓名、起始楼层、目的楼层、方向};设置电梯运动的方向、电梯的起始楼层

1、创建乘客链表C,确定电梯运动方向

2、将与电梯运动方向一致的乘客按上电梯的顺序组成链表A,将链表A中的乘客按下电梯的顺序组成链表B(若该方向上没有同方向的乘客,则确定电梯在该方向最终停留的楼层)

3、模拟电梯在该方向上乘客的变动情况,完成搭载的乘客的信息都从A、B、C中删除,随后确定电梯在该方向最终停留的楼层,并改变方向

如若C中还有乘客未搭载电梯,转到2,循环执行

#include<iostream>
#include<stdlib.h>
using namespace std;
/*乘客的结构体*/
struct passenger
{
    char name;
    int start;
    int end;
    int dir;
};

/*链表结构*/
typedef struct LNode
{
    passenger P;
    struct LNode *next;
}LNode, *LinkList;

/*将节点LN插入到链表C中,插入到头结点后面*/
void Insert(LNode *C, LNode *LN)
{
    LN->next = C->next;
    C->next = LN;
}

/*删除LN后的第一个节点*/
void del(LNode *LN)
{
    LNode *next2 = LN->next;
    LN->next = next2->next;
    free(next2);
}

/*在链表C中查找到与LN节点相同的节点,并删除*/
void finddel(LNode *LN, LinkList C)
{
    for (LNode *p = C; p->next != NULL; p = p->next)
    {
        if (p->next->P.name == LN->P.name)
        {
            del(p);
            break;
        }
    }
}

/*判断链表是否为空*/
bool isempty(LinkList L)
{
    if (L->next == NULL)
        return 1;
    else
        return 0;
}

/*当电梯运行方向为dir时,
将C中要进入电梯的节点全部存放至A中,按照进入电梯的顺序存放
并将其相应的存放在B中,按照离开电梯的顺序存放*/
void Insert2(LinkList A, LinkList B, LinkList C, int dir, int floor)
{
    for (LNode *p = C->next; p != NULL; p = p->next)
    {
        if (dir == 1 && p->P.start >= floor && p->P.dir == 1)
        {//查找出C中方向相同且起始楼层在电梯上方的所有节点 
            LNode *r = (LNode*)malloc(sizeof(LNode));
            LNode *s = (LNode*)malloc(sizeof(LNode));
            r->P = p->P;
            s->P = p->P;
            for (LNode *q = A; ; q = q->next)
            {//将该节点插入A中,确保其起始楼层为升序的方式 
                if (q->next == NULL || q->next->P.start >= r->P.start)
                {
                    Insert(q, r);
                    break;
                }
            }
            for (LNode *q = B; ; q = q->next)
            {//将该节点插入B中,确保其目标楼层为升序的方式 
                if (q->next == NULL || q->next->P.end >= s->P.end)
                {
                    Insert(q, s);
                    break;
                }
            }
        }
        else if (dir == 0 && p->P.start <= floor && p->P.dir == 0)
        {
            LNode *r = (LNode*)malloc(sizeof(LNode));
            LNode *s = (LNode*)malloc(sizeof(LNode));
            r->P = p->P;
            s->P = p->P;
            for (LNode *q = A; ; q = q->next)
            {
                if (q->next == NULL || q->next->P.start <= r->P.start)
                {
                    Insert(q, r);
                    break;
                }
            }

            for (LNode *q = B; ; q = q->next)
            {
                if (q->next == NULL || q->next->P.end <= s->P.end)
                {
                    Insert(q, s);
                    break;
                }
            }
        }
    }
}

int finds(int m, LinkList C, int dir)
{//确定出电梯行驶方向最后停留的楼层 
    if (dir == 1)
    {
        for (LNode *p = C->next; p != NULL; p = p->next)
        {
            if (m<p->P.start)
                m = p->P.start;
        }
    }
    else
    {
        for (LNode *p = C->next; p != NULL; p = p->next)
        {
            if (m>p->P.start)
                m = p->P.start;
        }
    }
    return m;
}

int main()
{
    LinkList C = (LNode*)malloc(sizeof(LNode));
    C->next = NULL;//C为存储所有的乘客信息的链表 
    LinkList A = (LNode*)malloc(sizeof(LNode));
    A->next = NULL;//A为存储某个行驶方向上,上电梯的所有乘客信息 
    LinkList B = (LNode*)malloc(sizeof(LNode));
    B->next = NULL;//B为A中乘客的按下电梯顺序排列 
    int floor;//记录电梯选择运行方向时所在的楼层 
    int dir;//记录电梯运行的方向 
    cout << "请输入电梯所在楼层:";
    while (cin >> floor)
    {
        /*输入乘客信息并创建链表,电梯运行的初始方向根据只要上方有乘客则向上*/
        cout << "请依次输入乘客代号、起始楼层、目标楼层、行驶方向:" << endl;
        char a;
        dir = 0;
        while (cin >> a)
        {
            LinkList x = (LNode*)malloc(sizeof(LNode));
            x->P.name = a;
            cin >> x->P.start >> x->P.end >> x->P.dir;
            Insert(C, x);
            if (x->P.start >= floor)
                dir = 1;
        }
        /*while(cin>>str)语句在结束输入时使用了Ctrl+Z,告诉cin用户已经结束了输入,
        为了让程序正常运行,调用cin.clear()让cin的所有条件状态位复位*/
        cin.clear();
        /*模拟电梯行驶过程*/
        while (!isempty(C))
        {//有乘客未乘坐电梯 
            Insert2(A, B, C, dir, floor);//将C中满足条件的乘客放入A、B中 
                                         /*乘客上下*/
            if (isempty(B))
                floor = finds(floor, C, dir);
            while (!isempty(B))
            {//模拟电梯往某个特定方向行驶的过程 
                if (dir == 1)
                {//当电梯是向上行驶时 
                    if ((A->next == NULL) || (A->next->P.start > B->next->P.end))
                    {//出电梯的情况 
                        cout << "当前楼层为" << B->next->P.end << ""
                            << B->next->P.name << "出电梯" << endl;
                        finddel(B->next, C);//删除C中对应的乘客信息 
                        del(B);//删除链表中该乘客的信息 
                    }
                    else
                    {//进电梯的情况 
                        cout << "当前楼层为" << A->next->P.start << ""
                            << A->next->P.name << "进电梯" << endl;
                        del(A);//删除链表中该乘客的信息
                    }
                }
                else
                {//当电梯是向下行驶时 
                    if ((A->next == NULL) || (A->next->P.start < B->next->P.end))
                    {
                        cout << "当前楼层为" << B->next->P.end << ""
                            << B->next->P.name << "出电梯" << endl;
                        finddel(B->next, C);
                        del(B);
                    }
                    else
                    {
                        cout << "当前楼层为" << A->next->P.start << ""
                            << A->next->P.name << "进电梯" << endl;
                        del(A);
                    }
                }
                if (B->next != NULL && B->next->next == NULL)
                {
                    floor = finds(B->next->P.end, C, dir);

                }
            }
            dir = !dir;//改变行驶方向
        }
        /**/
        cout << endl << endl;
        cout << "请输入电梯所在楼层:";
    }
    system("pause");
}

运行效果如下:

 

以上是关于电梯调度算法...的主要内容,如果未能解决你的问题,请参考以下文章

结对作业——电梯调度算法的实现与测试之需求分析与算法设计

算法解密:电梯是如何实现上下调度的?

双电梯电梯调度算法的简单分析

结对作业——电梯调度算法的实现与测试之需求分析与算法设计(未完待续)

电梯调度算法模拟

简单电梯调度算法