• 2017面向对象程序设计寒假作业3!


    实现简单电梯调度(2)

    GitHub:pullself
    承接上文:2017面向对象程序设计寒假作业2!


    上文调度方式的更新与优化

    由于现在电梯可以在任意楼层停靠并且上下人。进行对应的修改。
    建立在上文所使用的调度方式为基础,继续给出以预知和非预知为条件的两个程序。

    代码行数 调试bug 编码时间
    261行 1个 4h
    303行 4个 12h

    预知版本

    通过分析,我们可以知道,只需要对搜索方式进行修改即可,修改为通过接受到的请求,动态增加所需要搜索节点。

    具体实现方式:

    • 在搜索过程中加入目的地判断与记录。
    • 动态增加目的地停靠的搜索节点。
    • 有选择性的删除节点。

    实现难度不高。
    尾言:作为一个算法验证的版本,将不会在下一次的作业中进行更新。


    非预知版本

    (文章前半部分的重点)
    鉴于之前有考虑到后续的修改问题,所以在最终版本的时候将函数功能细分。

    程序依然是由7-8个函数组成:

    • dfs类:有原版dfs改造;
      • void sorting0(int m):全排列可执行的请求
      • void sprting1(int n):在全排列中插入目的地组成搜索全排列。
      • void sortrequest(void):创造排序搜索队列。
      • void caltime(void):在搜索中计算时间与请求状态更新。
      • void mintime(void):记录当前最优策略。
    • move类:由原版move改造;
      • int enter(void):判断有没人进入,以及可处理请求的删除。返回值为10代表有人或没人。
      • int exit0(void):判断是否有人出去,以及计算等待时间。返回值为10代表有人或没人。
      • int end(void):判断是否所有人都被扔出。

    分析过后,我们能知道需要修改的函数分别为sorting0,sorting1以及end。在此基础上,可以继续将几个搜索函数整合。

    最终修改为6个函数:

    void findsollution(int x);//搜索策略
    void mintime(void);//计算时间
    void sortrequest(void);//序列构造
    int enter(int floor);//进入判定
    int exit0(void);//出去判定
    int end(void);//结束判定
    

    在原先的程序上增加了可以做到任意层出入,留下了可以多请求,超重判定的预定策略。这个

    void findsollution(int x){
    	int i = 0;
    	int j = 0;
    	int boolean = 1;
    	int statusbackup[5] = {0};// "5" is required to be changed when wishing a new amount of requestions.
    	struct seteleStat eleStatBackup = {1};
    	
    	if(x == 0){
    		for(i = 0; i < requestionamount; i++){
    			if(info[i].asktime > eleStat.timeworked){
    				info[i].status = 2;
    			}
    		}
    	}
    	
    	if(eleStat.arp + eleStat.are != 0){
    		for(i = 0; i < eleStat.arp + eleStat.are; i++){
    			int floor = 0;
    			
    			eleStatBackup = eleStat;
    			for(j = 0; j < requestionamount; j++){
    				statusbackup[j] = info[j].status;
    			}
    			
    			if(i < eleStat.arp){
    				floor = eleStat.requestion[i];
    			} 
    			else{
    				floor = eleStat.reexit[i - eleStat.arp];
    			}
    			
    			eleStat.timeworked += abs(eleStat.position - floor);
    			eleStat.position = floor;
    			eleStat.sollution[eleStat.sollutioncount] = eleStat.position;
    			eleStat.sollutioncount ++;
    			if(exit0() + enter(eleStat.sollution[eleStat.sollutioncount - 1]) != 0){
    				eleStat.timeworked ++;
    			}
    			else{
    				continue;
    			}
    			findsollution(1);
    			
    			eleStat = eleStatBackup;
    			for(j = 0; j < requestionamount; j++){
    				info[j].status = statusbackup[j];
    			}
    		}
    	}
    	else{
    		if(end() == 1){
    			mintime();
    		}
    	}
    	
    	if(x == 0){
    		for(i = 0; i < requestionamount; i++){
    			if(info[i].asktime > eleStat.timeworked){
    				info[i].status = 0;
    			}
    		}
    	}
    }
    

    bug以及优化记录

    1. 优化:main()中每次eleStat.timeworked ++时直接continue,省去time0变量的额外使用。
    2. bug:无限循环数组下标无限增大,适当时机跳出循环。

    尾言

    由于该程序的最终目标是找到一个所有乘客等待时间总和最短的方案,但在上文中经过很多的测试数据后会发现:在不少情况下,有可能出现某个人的等待时间占据了所有人等待时间的50%以上。在现实中,这显然是不合理的。一个人的等待时间是有限的,若一个电梯反复的改变是一个人的等待时间过长,显得不公平。再通过分析现实中的电梯运行情况,就会发现现实中的电梯运行是有其的道理的。所以在此基础上再出发改造出一个代码。同时上文中的方案将不再更新。


    电梯模拟

    在现实的基础上给出的算法,在现实电梯的基础上给出尽可能优的运行策略。前提是非预知。同时准备在这个基础上对未来可能会产生的修改以及需求预先留下空间和数据。

    代码行数 调试bug 编码时间
    450行 5个 16h

    原来认为一个模拟现实的电梯考虑的内容可以少一点,但是却发现优化过程依然是比较繁琐的。
    思路比较简单:电梯只有上行和下行两个模式,就算是电梯处于停靠状态,它也依然处于其中一个模式中,有两个队列,上行队列以及下行队列,在收到请求后会构造出相应的队列,电梯运行中只需要执行队列当前操作,然后适当时机让元素出队列,进队列。更新队头队尾标记就行。(当电梯停靠1s后接受到的同层请求会让电梯多停靠1s)

    程序由3个函数构成:

    void cre_queue(int i,bool flag);//创建队列
    void cre_queue2(int i,bool flag);//创建队列 
    void sorting();//排序队列
    

    电梯的运行模拟被放置在主函数中运行。

    bug以及优化记录

    1. (优化和bug)创建队列的时候将重复的元素合并,防止出现队列元素重复运行。
    2. (bug)由于当检测到队列元素为0判定队列结束,开始改变运行方式。但是若一开始两个队列都没有元素的时候,电梯不会进行任何处理,就会导致电梯无法继续运行。
    if((up[pu.left].floor == 0) && (down[pd.left].floor == 0))
    	    {
    	    	ele.time ++;
    		}
    
    1. (bug)若上行队列最后一个元素与下行队列第一个元素相同(反之同理)会出现反复停靠。导致时间计算错误。
    2. (优化)由于电梯在转换策略后可能会继续反方向接人,在这个动作中依然可以顺带接人运行。

    文件输入输出

    由于烦人的春节的原因,学习的进度被滞后了很多……
    由于知识储备的不够,以下只是简单的学习后使用的方法。更详细的内容会在以后的学习笔记中记录。

    1. 首先是引入头文件fstream:fstream定义了用于文件输入的类ifstream和文件输出的类ofstream
    2. 读取文件:
      - 创建一个ifstream对象:ifstream fin;
      - 关联相应的文件:fin.open("input.txt");
      - 使用ifstream的方法读取文件:fin>>;
      - 结束关联:fin.close();
    3. 写入文件
      - 创建一个ofstream对象:ofstream fout;
      - 关联相应的文件:fout.open("output.txt");
      - 使用ofstream的方法写入文件:fout<<;
      - 结束关联:fout.close();
    4. 文件模式:
    常量 含义
    ios_base::in 打开文件,读取
    ios_base::out 打开文件,写入
    ios_base::ate 打开文件,将指针移动到文件尾
    ios_base::app 在文件尾写入
    ios_base::trunc 如果文件存在,清空文件内容
    ios_base::binary 二进制文件

    测试数据

     0 1 2
     0 1 3
     0 1 4
     0 1 5
     0 1 6
    

    同最早时间请求,同楼层请求。

     5 2 9
     12 4 9
     9 8 9
     21 3 9
     14 4 9 
    

    目的地相同

     1 1 10
     1 10 1
     1 1 10
     1 10 1
     1 1 10
    

    队列返回首尾同楼层

     25545 5 10
     43545 7 1
     15456 6 7
     2135 9 4
     54654 5 4
    

    请求间间断,检测电梯暂停后的运行。

     2 7 1
     3 8 4 
     5 5 7
     3 9 10
     9 1 6
    

    小型数据随机


    尾言

    不得不说,春节的事情是真的多,想好好的坐下来写东西的时间都几乎没有。心累……

  • 相关阅读:
    linux内核的若干问题
    shell(四)--turboastat
    Mac系统维护
    花卉养殖(1) 黄叶
    你就是佛(1)- 本体、开悟与思想
    linux 工具(2)----- crontab定时任务管理
    vim (四) 使用技巧
    linux kernel __init和__exit宏的作用
    优秀的网站
    Mac OSX 快捷键&命令行
  • 原文地址:https://www.cnblogs.com/pullself/p/8448538.html
Copyright © 2020-2023  润新知