• 数据结构习题集之银行业务模拟(双队列事件驱动)


    个人感受和想法:

    写这个课程设计很煎熬,时间话的太久,我就是一个容易死磕的笨蛋,一直不太理解离散事件驱动,在网上看了很多博客,感觉乱糟糟看不下去,有些对离散事件驱动感觉写的体现不明显,我就比较偏执所以主线是根据事件驱动函数执行的,其实这个里面涉及到一些解锁开锁的问题,操作系统里面的知识我忘记了许多,并且这个程序是模拟,初始条件随机,对于出现结果难于预期,于是感觉还是比较绕吧,但还是被我怼出来了!

    [问题描述]

    客户业务分为两种。第一种是申请从银行得到一笔资金,即取款或借款。第二种是向银行投入一笔资金,即存款或还款。银行有两个服务窗口,相应地有两个队列。客户到达银行后先排第一个队。处理每个客户业务时,如果属于第一种,且申请额超出银行现存资金总额而得不到满足,则立刻排入第二个队等候,直至满足时才离开银行;否则业务处理完后立刻离开银行。每接待完一个第二种业务的客户,则顺序检查和处理(如果可能)第二个队列中的客户,对能满足的申请者予以满足,不能满足者重新排到第二个队列的队尾。注意,在此检查过程中,一旦银行资金总额少于或等于刚才第一个队列中最后一个客户(第二种业务)被接待之前的数额,或者本次已将第二个队列检查或处理了一遍,就停止检查(因为此时已不可能还有能满足者)转而继续接待第一个队列的客户。任何时刻都只开一个窗口。假设检查不需要时间。营业时间结束时所有客户立即离开银行。

    写一个上述银行业务的事件驱动模拟系统,通过模拟方法求出客户在银行内逗留的平均时间

    [实现提示]
        事件有两类;到达银行和离开银行。韧始时银行现存资金总额为total。开始营业后的第—个事件是客户到达,营业时间从0到closetime。到达事件发生时随机地设置此客户的交易时间相距下一到达事件之间的时间间隔。每个客户要办理的款额也是随机确定的,用负值和正值分别表示第一类相第二类业务。变量total、closetime以及上述两个随机量的上下界均文互地从终端读入,作为模拟参数。两个队列和一个事件表均要用动态存储结构实现。注意弄清应该在什么条件下设置离开事件,以及第二个队列甩怎样的存储结构实现时可以获得较高的效率。注意:事件表是按时间顺序有序的。

    主程序代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <math.h>
    #include "LinkQueue.h"
    #include "EventList.h"
    int  Total;//银行拥有的初始存款(元)
    int  CloseTime;//营业时间(分钟)
    int  MaxAmount;//交易额度上限
    int  MaxDealTime;//最长交易时间
    int  MinDealTime;//最短交易时间
    int  MaxInterTime;//最大间隔时间
    int  MinInterTime;//最小间隔时间
    int  StayTime=0;//总的停留时间
    int  ClientNum=0;//完成交易的客户数
    int  Count=0;//抵达的客户数
    int  FailedNum=0;//未完成交易的客户数
    int  CurrentTime=0;//当前时刻
    ClientQueue Q1;//第一条队列
    ClientQueue Q2;//第二条队列
    Client LAST;//第二条队列队尾客户
    int State=0;//初始状态为0,空闲
    int Before;//顺序检测之前银行所有存款
    void OpenForDay();
    void BankSimulate();
    void Random(Client *C);//随机化客户交易金额和交易时间
    void NextClient();//生成下一个随机到达的客户
    void  CilentArrive(Event A);//到达事件的驱动函数
    void ClientDeparture(Event D);//离开事件的驱动函数
    void Deal(Client D,int StartTime);//交易函数
    void Run();//运行函数
    void Check();//检查函数
    void Print(Client C);//打印客户离开时的信息
    void CloseForDay();//关门函数
    void ClearQueue(ClientQueue *Q);//清空未完成业务的队列中的客户
    int main()
    {
        system("color 1e");
        srand(time(NULL));
        OpenForDay();
        BankSimulate();
        CloseForDay();
        return 0;
    }
    
    void OpenForDay()
    {
        printf("请输入银行拥有的额款:");
        scanf("%d",&Total);
        printf("请输入营业时间:");
        scanf("%d",&CloseTime);
        printf("请输入客户抵达最大时间间隔:");
        scanf("%d",&MaxInterTime);
        printf("请输入客户抵达最小时间间隔:");
        scanf("%d",&MinInterTime);
        printf("请输入最长客户业务办理时间:");
        scanf("%d",&MaxDealTime);
        printf("请输入最短客户业务办理时间:");
        scanf("%d",&MinDealTime);
        printf("请输入交易额度上限:");
        scanf("%d",&MaxAmount);
        InitEventList();//初始化事件表,采用单链表结果
        InitQueue(&Q1);//初始化第一条队列
        InitQueue(&Q2);//初始化第二条队列
        Event A;
        Count++;
        A.ClientCode=Count;
        A.EventType=1;//第一个必定为到达事件
        A.HappenTime=rand()%(MaxInterTime+1);//随机生成第一个客户到达事件的发生时刻
        OrderInSert(A);//插入事件表
        printf("
    银行流水:
    ");
        printf("客户编号	抵达时间	交易金额	交易时间	离开时间	等待时间	银行余额
    ");
        return;
    }
    void BankSimulate()//事件驱动函数,根据事件类别定义操作
    {
        EventList P=L->next;
        while(P!=NULL)//事件表为空时跳出循环
        {
            Event A=P->Data;//当前事件
            if(A.EventType==1)
                CilentArrive(A);//到达事件
            else
                ClientDeparture(A);//离开事件
            Check();//检查函数,当state==2时对于队列2执行
            Run();//运行操作,当State==0时对于队列1执行
            P=P->next;
        }
        return;
    }
    
    void  CilentArrive(Event A)
    {
        CurrentTime=A.HappenTime;
        NextClient();//生成到达函数
        Client C;//根据到达事件,生成一个客户入队列
        C.ClientCode=A.ClientCode;//到达事件编号和到达客户编号一致
        C.ArriveTime=A.HappenTime;//当然时间也一致
        Random(&C);//随机生成客户交易时间和交易额度
        EnQueue(&Q1,C);//无论状态,先进去第一条队列再说
        return;
    }
    
    void ClientDeparture(Event L)
    {
        CurrentTime=L.HappenTime;//离开时间时间
        Client D;
        if(State==1)//第一条队列队首客户交易完成,准备离开
        {
            DeQueue(&Q1,&D);//队首元素出队列
            if(D.DealAmount>0&&!QueueEmpty(&Q2))//如果交易额度为正,且第二条队列不为空,则进入State2
            {
                State=2;//状态2对队列二进行检查
                Before=Total-D.DealAmount;//记下在之前一个客户交易前的银行余额
                LAST=Q2.rear->Data;//记下第二个队列队尾客户编号,便于控制检查结束
            }
            else
                State=0;//不满足条件又进入状态0,对第一条队列进行Run
        }
        else
        {
            DeQueue(&Q2,&D);//如果State==3进入此,说明队列2中的客户在检查中发现满足条件完成交易,并且在队首
            if(QueueEmpty(&Q2)||Total<=Before||D.ClientCode==LAST.ClientCode)//队首出队列后进行条件检查
                    State=0;//队列空,或者金额笑了,或者到了最后一个客户了,则变成状态0,进入队列1操作
            else
                    State=2;//否则继续变成2,对队列2进行检查操作
        }
        Print(D);//打印出客户离开信息
        return;
    }
    
    void Run()
    {
        if(QueueEmpty(&Q1)||State!=0)
            return;//队列1为空或者State!=0,无法对队列1进行Run
        Client D;
        QueueHead(&Q1,&D);//先读取队首客户
        while(D.DealAmount<0&&abs(D.DealAmount)>=Total*0.2)//队首客户是取钱,且取得比较多的话
        {
            DeQueue(&Q1,&D);//从队列1出来,然后进入队列2
            EnQueue(&Q2,D);
            if(QueueEmpty(&Q1))//队列空了就退出了,可能很多客户都是取钱比较多,都要进入队列2
                return;
            QueueHead(&Q1,&D);
        }
        Deal(D,CurrentTime);//直到有一个客户不满足以上条件,不用进入队列2,则进行交易
        return;
    }
    
    void Check()
    {
        if(State!=2)//在交易额度大于0之后对队列2进行检查的状态
            return;
        Client W;
        QueueHead(&Q2,&W);
        while(abs(W.DealAmount)>=Total*0.2)
        {
            DeQueue(&Q2,&W);//仍然不满足条件,就跳到队尾
            EnQueue(&Q2,W);
            if(W.ClientCode==LAST.ClientCode)//到了之前标记的最后一个客户,说明检查过一遍了
            {
                State=0;//进入状态0,对队列进行操作
                return;//退出函数
            }
            QueueHead(&Q2,&W);//否则继续检查
        }
        Deal(W,CurrentTime);//对检查到的满足条件的进行交易处理
        return;
    }
    
    void Deal(Client D,int StartTime)
    {
        if(State==0)//对队列一中的客户交易
             State=1;
        else//State=2对队列二中的客户进行交易
             State=3;
        Event L;
        L.ClientCode=D.ClientCode;
        L.EventType=0;//离开事件
        L.HappenTime=StartTime+D.DealTime;//离开事件发生时间
        if(L.HappenTime<CloseTime)//如果预计交易时间超过了关门时间,则不进行交易
        {
            Total=Total+D.DealAmount;
            StayTime+=StartTime-D.ArriveTime;//累加等待时间
            ClientNum++;//完成交易客户数++
            OrderInSert(L);//离开事件按时间有序插入事件表
        }
        return;
    }
    
    void CloseForDay()
    {
        printf("
    数据统计:
    ");
        printf("抵达银行人数为%d人
    ",Count);
        printf("成功办理业务为%d人
    ",ClientNum);
        double SuccessRate=(double)ClientNum*100/Count;
        printf("业务办理成功率为%2.2lf%%
    ",SuccessRate);
        double AveTime=StayTime/ClientNum;
        printf("平均等待时间为%.2lf分钟
    ",AveTime);
        printf("第一条队列Q1");
        ClearQueue(&Q1);
        printf("第二条队列Q1");
        ClearQueue(&Q2);
        printf("未完成交易的客户数为%d
    ",FailedNum);
        double FailedRate=(double)FailedNum*100/Count;
        printf("业务办理失败的概率为%2.2lf%%
    ",FailedRate);
        printf("银行余额为%d元",Total);
        return;
    }
    
    void Random(Client *C)
    {
         C->DealTime=rand()%(MaxDealTime-MinDealTime+1)+MinDealTime;//随机生成交易时间
         C->DealAmount=rand()%(2*MaxAmount+1)-MaxAmount;//随机生成交易额度,注意可正可负的做法
         return;
    }
    
    //下一个客户抵达事件
    void NextClient()
    {
        Event B;
        int NextTime=CurrentTime+rand()%(MaxInterTime-MinInterTime+1)+MinInterTime;//随机生成下一次到达时刻
        if(NextTime<CloseTime)//如果下一个客户到达时间超过关门时间,则不允许生成该达到事件
        {
            Count++;//达到客户++
            B.ClientCode=Count;//达到事件编号
            B.EventType=1;
            B.HappenTime=NextTime;//到达事件发生时间
            OrderInSert(B);//到达时间按时间顺序插入事件表
        }
        return;
    }
    
    void Print(Client C)
    {
        printf("%-8d	%-8d	%-8d	%-8d	%-8d	%-8d	%-8d	
    ",C.ClientCode,C.ArriveTime,C.DealAmount,C.DealTime,CurrentTime,CurrentTime-C.DealTime-C.ArriveTime,Total);
        return;
    }
    
    void ClearQueue(ClientQueue *Q)//关门时驱散未完成交易的客户,即销毁队列
    {
        printf("未完成业务交易的客户:
    ");
        Client E;
        if(!QueueEmpty(Q))
            printf("客户编号	抵达时间	交易额度	等待时间	
    ");
        else
            printf("无
    ");
        while(!QueueEmpty(Q))
        {
            DeQueue(Q,&E);
            FailedNum++;
            printf("%-8d	%-8d	%-8d	%-8d	
    ",E.ClientCode,E.ArriveTime,E.DealAmount,CloseTime-E.ArriveTime);
        }
        DestroyQueue(Q);
        return;
    }
    

    事件链表的头文件:

    #ifndef EVENTLIST_H_INCLUDED
    #define EVENTLIST_H_INCLUDED
    #define TURE 1
    #define FALSE 0
    #define OK 1
    #define ERROR 0
    typedef int Status;
    typedef struct{
        int ClientCode;//客户编号
        int HappenTime;//事件发生时间
        int EventType;//0为离开事件,1位到达事件
    }Event;
    
    typedef struct ENode{
        Event Data;
        struct ENode *next;
    }ENode,*EventList;
    EventList L;//事件链表,按时间有序
    
    Status InitEventList()
    {
        L=(EventList)malloc(sizeof(ENode));
        if(!L)
            exit(OVERFLOW);
        L->next=NULL;
        return OK;
    }
    Status Compare(Event A,Event B)
    {
        if(A.HappenTime<B.EventType)
            return -1;
        if(A.HappenTime>B.HappenTime)
            return 1;
        return 0;
    }
    
    Status OrderInSert(Event T)
    {
        EventList Q=L;
        EventList P=Q->next;
        while(P!=NULL)
        {
            if(Compare(P->Data,T)==1)
                break;
            P=P->next;
            Q=Q->next;
        }
        EventList  S=(EventList)malloc(sizeof(ENode));
        S->Data=T;
        S->next=P;
        Q->next=S;
        return OK;
    }
    
    
    #endif // EVENTLIST_H_INCLUDED
    

    队列的头文件:

    #ifndef EventQueue_H_INCLUDED
    #define EventQueue_H_INCLUDED
    #define TURE  1
    #define FALSE 0
    #define OK 1
    #define ERROR 0
    typedef int Status;
    typedef struct{
        int  ClientCode;//客户编号
        int  DealAmount;//客户交易额度
        int  ArriveTime;//到达时间
        int  DealTime;//交易时间
    }Client;
    
    //队列结点
    typedef struct CNode{
        Client Data;
        struct CNode *next;
    }CNode,*ClientPtr;
    
    //队列结构体
    typedef struct{
        ClientPtr front;
        ClientPtr  rear;
    }ClientQueue;
    
    //初始化队列
    Status InitQueue(ClientQueue *Q)
    {
        Q->front=(ClientPtr)malloc(1*sizeof(CNode));
        if(!Q->front)
            exit(OVERFLOW);
        Q->rear=Q->front;
        Q->front->next=NULL;
        return OK;
    }
    //客户进入队列
    Status EnQueue(ClientQueue *Q,Client E)
    {
        ClientPtr New=(ClientPtr)malloc(sizeof(CNode));
        if(!New)
            exit(OVERFLOW);
        New->Data=E;
        New->next=NULL;
        Q->rear->next=New;
        Q->rear=New;
        return OK;
    }
    //客户出队列
    Status DeQueue(ClientQueue *Q,Client *E)
    {
        if(Q->front==Q->rear)
            return ERROR;
        ClientPtr Head=Q->front->next;
        *E=Head->Data;
        Q->front->next=Head->next;
        if(Q->rear==Head)
            Q->rear=Q->front;
        free(Head);
        return OK;
    }
    
    Status DestroyQueue(ClientQueue *Q)
    {
        while(Q->front)
        {
            Q->rear=Q->front->next;
            free(Q->front);
            Q->front=Q->rear;
        }
        return OK;
    }
    Status QueueHead(ClientQueue *Q,Client *D)
    {
         if(Q->front==Q->rear)
            return ERROR;
        ClientPtr Head=Q->front->next;
        *D=Head->Data;
        return OK;
    }
    Status QueueEmpty(ClientQueue *Q)
    {
        if(Q->front==Q->rear)
            return TURE;
        return FALSE;
    }
    #endif // EventQueue_H_INCLUDED
    

    接下来是结果:

    大功告成,继续推进自己的任务!可能存在Bug,发现请告知!

    有问题请私信博主我:QQ1594047159 

  • 相关阅读:
    hive基本操作与应用
    用mapreduce 处理气象数据集
    熟悉常用的HBase操作,编写MapReduce作业
    爬虫大作业
    第三章 熟悉常用的HDFS操作
    数据结构化与保存
    获取全部校园新闻
    爬取校园新闻首页的新闻的详情,使用正则表达式,函数抽离
    网络爬虫基础练习
    Hadoop综合大作业
  • 原文地址:https://www.cnblogs.com/zhichao-yan/p/13368505.html
Copyright © 2020-2023  润新知