分类

课内:
不限
类型:
不限 毕业设计 课程设计 小学期 大作业
汇编语言 C语言 C++ JAVA C# JSP PYTHON PHP
数据结构与算法 操作系统 编译原理 数据库 计算机网络 软件工程 VC++程序设计
游戏 PC程序 APP 网站 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
年份:
不限 2018 2019

资源列表

  • 基于C++的一元多项式的计算

    1 问题描述将多个多项式输入并存储在内存中,然后对多项式进行加、减运算并输出结果。
    2 数据结构描述
    一元多项式的表示方式:如 f=6x^5-7x^4+3 表示为 f=6,5 -7,4 3,0
    每个多项式用单链表来存储:
    typedef struct PolyNode { float coef; // 系数 int expn; // 指数 struct PolyNode *next; // 指向下个结点的指针 }Poly;
    图示举例


    3 主要算法描述
    主要功能:对输入的一元多项式进行加减运算并输出
    程序主要模块:如下图所示


    3.1 模块一:一元多项式的建立
    结点类型为:结构体Poly
    数据流:系数、指数。
    函数名为:
    int CreatPoly(P_Poly*, int);

    3.2 模块二:两个一元多项式的加法
    数据流:两个一元多项式相加后的结果
    函数名为:
    void AddPoly(P_Poly*, P_Poly, P_Poly);


    3.3 模块三:两个一元多项式的减法
    数据流:两个一元多项式相减后的结果;
    函数名为:
    void DecPoly(P_Poly*,P_Poly,P_Poly);


    程序中的主要函数及相应功能:
    // 创建一元多项式int CreatPoly(P_Poly*, int);// 输出一元多项式void OutP_Poly(P_Poly); // 两个多项式求和void AddPoly(P_Poly*, P_Poly, P_Poly); // 两个多项式求差void DecPoly(P_Poly*, P_Poly, P_Poly);
    4 程序的调试4.1 问题一由于程序运用了输入输出流的控制符,而没有包含<iomanip>文件,所以在连接时,出了错,后来看了下面窗口的错误提示,才知道自己忘了包含该文件进去。
    4.2 问题二因为输入多项式每一项时的格式为”系数,指数”,而用cin语句不知怎么解决,后来查找资料获悉用scanf函数可以实现输入的格式控制,因此运用了scanf这一函数。
    4.3 问题三输出多项式时,要输出正负号,这是一个比较麻烦的问题。开始想到了运用输出控制符setiosflags(ios::showpos)直接输出正号。可是却发现启用了该控制符后对后面的输出也起作用,导致x后面的指数在输出时也带上了正号。左思右想,得不到解决办法,只好另寻方法。最后多项式是可以输出来了,可是每次输出完后随即弹出出错框(如下图),这个问题本人至今还想不透。

    5 程序测试多项式名称不符合时的情况(本程序规定用单个小写字母命名多项式),如:命令输出一元多项式A

    多项式不存在时的输出情况,如:命令输出一元多项式a

    两多项式相加,如:a=6x^6-10x^5+4x^2 b=-x^6+5x^5+6x^4+2x^3

    两多项式相减,如:a=-6x^7+10x^6+4x^4 b=5x^6-9x^5+5x^4+3x^3

    6 程序特色
    本程序的力求简单明了,让使用者易于掌握。因此每一个指令都有相应的提示语句,分别按照这些提示语句进行选择,就会出现相应的菜单或实现相应的功能。
    原本想加上乘法和除法的运算,可是每项互乘除要求两个链表的节点数据也相乘除,问题太多,又想不到好的解决办法,想不出具体实现的函数,因此,没有进行功能扩展。这也是本程序最大的不足之处。
    每个函数对应解决一个问题,模块分明,系统出现错误时,易于限定哪个函数还不能完美实现某个功能,以便修改并改进。在完善程序的时候,这个特点帮了我很大的忙。例如调试减法运算功能时,系统有时会出现错误,因此便锁定在相减的函数上查找不足。又如,自己对输出这一功能不是很满意,要改进的话则只需在输出函数上做改进。

    7 课程设计总结这一次的课程设计,我再次感到了程序世界的美妙。
    记得在分析完问题的要求后,对要写的程序进行了初步的模块划分。当时觉得问题看起来很简单,但是在具体实现的函数编写上遇到了很多的细节问题。在着手解决这些问题的过程中,虽然耗费了不少时间,困难重重,但付出了总会有收获,即使有一些问题自己还没有十分满意的解决掉,但在无形之中自己又增加了不少课外知识和一些以前忽略的知识点,拓宽了自己的视野,提升了自己的动手能力。
    最主要的是对单向链表的使用有了更深刻的了解和掌握,因为多项式的计算要求用单向链表来实现,所以在做课程设计之前通过网上找资料,查参考书等对单向链表的使用比以前掌握的更加到位,并且能够灵活运用,通篇几乎用的都是单向链表,这是受益的一大重点。
    参考资料[1] 数据结构教程学习指导(第3版). 李春葆. 北京: 清华大学出版社, 2009
    [2] C++程序设计. 谭浩强. 北京: 清华大学出版社, 2008
    [3] C++程序设计题解与上机指导. 谭浩强. 北京: 清华大学出版社, 2007
    [4] C++程序开发例题与习题. 张基温. 北京: 清华大学出版社, 2001
    1 评论 5 下载 2018-10-31 16:25:20 下载需要3点积分
  • 基于JAVA实现的业务员业绩管理系统

    1 需求分析1.1 问题描述为了方便用户对业务员的管理,现需开发对业务员的管理系统,系统主要解决的问题有:

    能让用户便于插入新的业务员信息
    能让用户便于删除离职的业务员信息
    能让用户便于对业务员业绩进行查看
    能让用户查看所有业务员的信息

    1.2 需求分析
    输入数据建立信息表
    查询满足要求的信息
    插入新的信息
    删除不需要的信息
    查看所有信息
    统计和排序功能
    删除后需要刷新系统
    退出系统

    2 概要设计2.1 主界面设计当点击相应的操作时(比如:0-7),通过在主程序中选择并调用对应的子方法程序以及其他方法(比如:UI类中的显示方法)中的辅助调用,实现并完成各自的功能操作(比如:添加、查找、删除、显示、、刷新、退出等)。
    系统主界面如下:

    2.2 存储结构设计依据给定的数据格式,个人信息由六个字段来组成,即编号、姓名、性别、部门、职务、工资。
    例如:



    编号
    姓名
    性别
    部门
    工资




    3
    张三

    开发部
    12000



    通过Java语言把学生的信息用集合形式连接,源程序要有适当的注释,使程序易于阅读,用文件进行数据储存。同时进行各种修改,完成信息管理等功能。
    2.3 系统功能设计依据程序的数据结构和功能,遵照“面向对象”原则,描述该程序的层次结构,在总体上包括数据的插入、添加、删除、查找、更改和数据的排序以及数据的显示等功能模块。

    输入类功能:通过输入各项数据给数据元素,来建立一个数据表
    显示类功能:输出表中所有节点的信息
    查找类功能:按照指定关键字,对相应学生信息进行查找
    删除类功能:查找到要删除学生的相应信息,并将其从表中永久的删除
    主类功能:调用以上功能类,并用监听机制选择性的调用

    3 模块设计3.1 系统子程序及功能设计new Insert();//建立信息表调用插入类new Insert();//插入新业务员信息调用插入类new Query();//查询Delete d = new Delete();//删除d.delete();QueryPay qp = new QueryPay();//按照工资排序qp.pay();new QueryAll();//按照ID排序System.exit(0);//退出ReName rn = new ReName();//改文件名字以确定删除rn.rename();
    3.2 系统功能图
    4 详细设计监听机制:监听到点击的类型执行相应的操作
    public void actionPerformed(ActionEvent e) { //监听到事件 if(e.getSource()==b1){ new Insert();//建立信息表调用插入类 } else if(e.getSource()==b2){ new Insert();//插入新业务员信息调用插入类 } else if(e.getSource()==b3){ new Query(); } else if(e.getSource()==b4){ Delete d = new Delete(); d.delete(); } else if(e.getSource()==b5){ QueryPay qp = new QueryPay(); qp.pay(); } else if(e.getSource()==b6){ new QueryAll(); } else if(e.getSource()==b){ System.exit(0);//退出 } else{ ReName rn = new ReName(); rn.rename(); } }
    主界面构造方法;
    UI(){ f = new JFrame("业务员业绩管理系统"); f.setSize(360,455);//设置大小 cp = f.getContentPane();//加载面板 cp.setLayout(new FlowLayout(1));//改成流式布局 setLayout(new FlowLayout()); label = new JLabel("欢迎使用业务员业绩管理系统"); cp.add(label); b1 = new JButton("1、 建立业务员信息表");//创建按钮 b1.setPreferredSize(new Dimension(175,25)); b1.setBounds(100,150,175,25); cp.add(b1);//添加按钮 b1.addActionListener(this);//添加监听机制 b2 = new JButton("2、 插入新业务员信息");//创建按钮 b2.setPreferredSize(new Dimension(175,25)); cp.add(b2);//添加按钮 b2.addActionListener(this);//添加监听机制 b3 = new JButton("3、查询业务员信息记录");//创建按钮 b3.setPreferredSize(new Dimension(175,25)); cp.add(b3);//添加按钮 b3.addActionListener(this);//添加监听机制 b4 = new JButton("4、 删除业务员信息");//创建按钮 b4.setPreferredSize(new Dimension(175,25)); cp.add(b4);//添加按钮 b4.addActionListener(this);//添加监听机制 b5 = new JButton("5、 业务员业绩排序");//创建按钮 b5.setPreferredSize(new Dimension(175,25)); cp.add(b5);//添加按钮 b5.addActionListener(this);//添加监听机制 b6 = new JButton("6、显示所有业务员信息");//创建按钮 b6.setPreferredSize(new Dimension(175,25)); cp.add(b6);//添加按钮 b6.addActionListener(this);//添加监听机制 b7 = new JButton("7、 刷新删除");//创建按钮 b7.setPreferredSize(new Dimension(175,25)); cp.add(b7);//添加按钮 b7.addActionListener(this);//添加监听机制 b = new JButton("0、 退出管理系统");//创建按钮 b.setPreferredSize(new Dimension(175,25)); cp.add(b);//添加按钮 b.addActionListener(this);//添加监听机制 f.setVisible(true);//显示窗体 f.setLocationRelativeTo(null);//窗体居中 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭}
    5 测试分析各功能的运行结果:
    5.1 建立信息表
    5.2 插入信息
    5.3 查询信息
    5.4 删除信息
    5.5 业绩信息
    6 工作总结只有动手了才知道自己原来这么菜,用java实现了本次的课设,但是很艰辛啊,好多东西边学边用,知识到用时方恨少。
    以后在其他课程的学习当中会记住这次的教训,也会记住学计算机的是动手学会的光看不练永远都是菜鸟一枚。
    参考文献[1] 史济民,顾春华. 软件工程——原理、方法与应用[M]. 北京:高等教育出版社,2004.
    [2] 张海藩. 软件工程导论[M]. 北京:清华大学出版社,2005.
    1 评论 10 下载 2018-10-31 16:09:15 下载需要11点积分
  • 基于C语言的模拟猜单词游戏

    一、课题内容和要求“模拟猜单词游戏”系统要求用C或C++模拟猜单词游戏。游戏包括:单词管理、玩家纪录、猜词过程、系统设置等必须完成的基本功能以及成绩计算、猜词时间限制、词库管理等选做的扩展功能。
    二、基本要求
    单词管理:程序中用来做谜题的单词必须存放在磁盘文件中
    猜词过程:

    先请用户输入猜的单词数量,可以有一个默认值随机抽取单词,对每个单词,系统根据谜底单词长度在屏幕上显示相应个数’-‘,假设谜底单词为”hello”,则在屏幕上输出”——-“玩家输入一个字母进行猜测,如果这个字母不在单词中,系统提示玩家不对;如果猜对字母,比如玩家输入了一个’l’,则在屏幕上输出”—ll-“重复3,直到玩家在规定的次数内猜出了单词或者超过次数游戏失败显示玩家每个单词猜对与猜错次数等统计信息。如果玩家猜出单词,计算成绩,如进入前五名提示玩家并记录存储到记录文件中询问玩家是否开始新一轮猜词,如果玩家选“否”,则系统退到外面的菜单
    系统设置:猜词次数可以为默认值。玩的时候,可以对其进行修改

    二、需求分析模拟猜单词游戏系统的功能框架图如下图所示。


    进入主菜单:输出菜单,用于显示若干个可选的功能选项。根据客户输入的选项来运行不同的功能,运行不同的函数
    进入游戏功能:用户进行游戏。系统随机提取单词,并提示玩家输入字母进行猜测,并匹配单词是否正确,若正确,输出正确对应的单词,否则提示错误,并提示还有几次出错机会。若游戏结束,显示游戏统计信息,然后询问是否再一次游戏
    设置功能:进入设置选项,可更改系统默认的玩家名字和默认的每一次游戏所要猜的单词数
    查看功能:进入查看选项,可查看历史玩家记录排名以及游戏版权信息
    退出功能:退出游戏

    三、概要设计3.1 主要结构体及全局变量struct player //玩家信息结构{ char name[10]; //玩家名字 double time; //玩家所用时间 double score; //玩家成绩 };typedef struct player player;char name[]="Player"; //全局变量,储存玩家姓名char word[10]; //全局变量,储存随机提取的单词int NUMBER=2; //全局变量,储存一次游戏猜单词的个数 player allPlayer[100]; //全局结构体数组,储存从文件中提取的所有玩家信息player playerTemp; //全局结构体变量,用于临时储存操作玩家的信息char tempWord[100][20]; //全局字符二维数组,用于储存玩家所猜的单词
    3.2 主要函数流程图随机提取单词函数:用于从文本中读取单词到内存中,并随机从内存中抽取单词。随机提取单词函数流程图如下图所示。

    游戏过程中单词判断函数:用于对玩家输入的字母进行判断,玩家先对一个单词进行猜测,如果玩家在规定次数内输出单词,则进行下一个单词的判断,直到所有单词都判断过为止,如果玩家把所有单词都输入正确,则转到游戏成功界面,显现玩家的成绩,并显现正确单词,否则转到失败界面,告知玩家GAME OVER,并显现正确单词。

    四、测试数据及其结果分析运行后,程序的主菜单界面如下。
    4.1 欢迎界面
    4.2 菜单界面
    4.3 游戏开始界面

    4.4 设置界面
    4.5 查看界面
    4.6 退出界面
    4.7 测试数据及其分析先进入游戏开始界面,开始第一回合,第一回合游戏的最终过程如下:

    开始第二回合时,游戏过程如下:

    最后显现成绩为:

    下面分别有由玩家姓名为Jack,Tom,Mike,Jhon,Ken的五名玩家进行游戏体验,且他们都通过了猜词测试,他们的最终成绩通过“查看”中的“排名”选项显示如下:

    如果已经完成猜单词游戏,不想继续进行,可以将选择查看直接查看排名,然后再退出。如果不想查看其他的信息的话,可以直接选择退出。
    4.8 结果分析如下
    在输入主菜单选项时如果输入的内容不是1-4之间的数字,而是其他数字或字母,系统将进行报错,提醒用户输入正确的操作内容
    如果用户在进行游戏时只猜出一个单词,则系统不进行提示用户输入成功,必须全部输入正确才进行显现成功提示
    在进行游戏玩家的成绩排名时,只有有成绩的玩家才可以进行排名,并且只有前五名的玩家的成绩才可以输出至屏幕上显现给玩家
    在玩猜单词游戏时会有三个闪屏信息提醒玩家做好游戏准备,并告知玩家一些游戏必知信息,让玩家有心理准备

    五、调试过程中的问题
    随机提取单词时,随机读取单词出现问题。因为文件指针使用很容易造成指针指向不明的情况,为避免此类问题的出现,通过放弃随机使用指针指向文件字母来读取完整的单词的随机抽取单词方法,改为先读取完所有字母到内存数组中,然后产生随机数来随机提取内存数组中的完整单词,最后成功随机提取单词。
    读入玩家信息到已建立的玩家信息记录本时,由于结构体变量定义错误导致不能读入,然后我们尝试定体结构体全局变量,这样就方变了程序中的多个函数都能够调用,并且每个函数进行调用时将改过的信息都能够输入到玩家信息记录本中,方便了用户在游戏主界面进行信息的查询。
    进入游戏界面进行游戏,最开始设置时程序逻辑上出现了问题:如果玩家第一个单词输入正确,在猜第二个单词时,有可能造成第二个单词输入正确时没有显示成功界面,直到错误次数到了规定次数才退出游戏界面,为解决这个问题,程序中定义了一个临时记录正确次数的变量tenpCorrectTimes,每猜一个单词时,就初始化为0,每猜对一个字母,就自增一,游戏中判断是否等于正在猜单词的长度,然后判断玩家是否成功猜对单词。
    玩家进入菜单界面,如果不小心敲了键盘上某个按键,这时可能出现无数次循环显示“输入错误”提示信息,为解决这类问题,程序从新设定输入类型,由最初的整形改为字符型,这样就能避免由于整形输入而输入字母时无法判断所造成的无限次输出“错误输入”提示信息,改为字符型输入就能判断所有的字符是否输入错误,从而解决了问题。
    在将玩家信息记录本中的玩家信息读取出来进行冒泡法排序时,由于一开始调用fread函数进行玩家信息的读取时,容易出现一系列错误,都是由于fread就函数的要求较为严格,一般读取二进制文件,后来老师叫我们用fscanf函数进行格式化读写,但是由于一点小细节没有注意,第文件内容只有一个结构变量内容的话,用fscanf函数进行读取,读取完之后,fp文件指针指向末尾,!feof(fp)函数判断为0,就不能将结构体变量的内容赋给结构体数组,这样就不能进行冒泡法排序,不能判断玩家的名次。后来再第一个fscanf后直接赋值,就解决只有一个玩家信息时读取失败的问题。

    六、课程设计总结做一个完整的游戏系统非常不容易,不仅要考虑游戏本身的游戏性问题,还要考虑跟这个游戏有关的一些问题,比如主菜单界面,设置问题,提示信息等等。开始做这个系统是并没有考虑这些,只考虑了游戏本身几个函数的实现,如猜词游戏中随机提取文件中的单词函数以及进入游戏的游戏主体函数。但随着游戏的不断完善,在不断排除几个主要函数在逻辑上的错误的过程中,以及在投入力量做界面优化方面时,发现做一个完整游戏所需要满足玩家对游戏体验的附带功能必不可少。通过这次游戏系统的开发,在和同学的合作中让我们受益很多。
    所谓学以致用,这次的系统开发让我知道C语言不仅仅是书上那几个示例那么简单,C语言所能做的事远远超出了我们这些初学者的想象。这次这个简单游戏,几乎用到了书上所学的知识,如一维数组、二维字符数组、各种循环、指针、文件的写入及读出等等。让我们对课本的理解更深刻。
    在具体到函数书写的时候,会碰到一些问题。除了一些语法错误,最令人头疼的应该是程序逻辑上的问题。如果逻辑上除了一丁点小错误,将导致整个函数无法正常运行,常常导致一些意外的结果。但这些逻辑上的小错误隐藏得非常深,如果不仔细耐心调试,是很难发现问题所在。这些问题常常导致我们感到不知所措,甚至导致我们产生放弃的念头,但经过我们的细心调试,终于把那些逻辑上的小错误都一一解决了。当程序运行正确时,我们在欣喜若狂的同时,也感触良多。
    这次的系统设计让我们学到了很多,除了对C语言的更深入理解之外,最大的收获是渐渐学会如何和他人合作。如今社会工作中不会别人合作是不可能的,一个人单打独斗是很难成功。唯有合作,才能双赢。
    这就是我们做系统的总结,虽不多,但很实在。
    1 评论 11 下载 2018-10-31 16:03:36 下载需要6点积分
  • 基于JAVA实现的Huffman哈夫曼树编码与解码

    1 概述给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
    哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫做Huffman编码(有时也称为霍夫曼编码)。
    1.1 设计目的及意义《数据结构》课程主要介绍最常用的数据结构,阐明各种数据结构内在的逻辑关系,讨论其在计算机中的存储表示,以及在其上进行各种运算时的实现算法,并对算法的效率进行简单的分析和讨论。进行数据结构课程设计要达到以下目的:

    了解并掌握数据结构与算法的设计方法,具备初步的独立分析和设计能力
    提高综合运用所学的理论知识和方法独立分析和解决问题的能力
    训练用系统的观点和软件开发一般规范进行软件开发,培养软件工作者所应具备的科学的工作方法和作风

    那么在功能方面,Huffman编码处理的是字符以及字符对应的二进制的编码配对问题,分为编码和解码,目的是压字符对应的二进制数据长度。我们知道字符存贮和传输的时候都是二进制的(计算机只认识0/1),那么就有字符与二进制之间的mapping关系。字符属于字符集(Charset),字符需要通过编码(encode)为二进制进行存贮和传输,显示的时候需要解码(decode)回字符,字符集与编码方法是一对多关系(Unicode可以用UTF-8,UTF-16等编码)。理解了字符集,编码以及解码,满天飞的乱码问题也就游刃而解了。
    以英文字母小写a为例,ASCII编码中,十进制为97,二进制为01100001。ASCII的每一个字符都用8个Bit(1Byte)编码,假如有1000个字符要传输,那么就要传输8000个Bit。问题来了,英文中字母e的使用频率为12.702%,而z为0.074%,前者是后者的100多倍,但是确使用相同位数的二进制。可以做得更好,方法就是可变长度编码,指导原则就是频率高的用较短的位数编码,频率低的用较长位数编码。Huffman编码算法就是处理这样的问题。
    1.2 功能及要求1.2.1 功能方面我们首先根据要编码的文件中字符出现的频率生成对应的哈夫曼编码;然后得到采用哈夫曼编码后的目标文件,并保存;接下来根据要解码的文件对应的哈夫曼码表对文件进行解码;最终得到解码后的目标文件并保存。
    1.2.2 设计要求
    符合课题要求,实现相应功能
    要求界面友好美观,操作方便易行
    注意程序的实用性、安全性。

    2 需求分析及总体设计我们的需求是运用哈夫曼编码的相关知识对任意文本文件进行编码、解码。

    编解码:

    首先获取文本文件然后通过构建哈夫曼树得到一个字符-编码一一对应的HashMap,并保存至二进制文件最后按照该HashMap对读取的文件进行编码和解码,并分别进行保存
    界面使用Java的Swing和javafx来进行构架
    与本地内存交互是将解码的结果与Map压缩为.dat文件进行保存

    2.1 数据结构的设计2.1.1 哈夫曼树首先将所有字符看成是一个森林(每棵树仅有一个结点)。然后在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和。再从森林中删除选取的两棵树,并将新树加入森林。重复②、③步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
    2.1.2 哈夫曼编码算法首先获取文本文件中每个字符出现的次数。然后按照次数将字符对象进行排序。再进行哈夫曼树的构建。然后左0右1从root开始编码。最终得到字符-编码一一对应的一个数据集合。
    2.1.3 HashMap首先HashMap采用链式存储结果。然后每个链节点包含两个对象:Character(用来存字符),Sring(用来存对应的编码)。
    2.1.4 UI界面设计首先我们采用JavaSwing进行UI界面搭建。
    2.2 系统功能框架下图为HuffMan树编解码程序整体功能实现的流程图。

    我们首先从本地内存读取文本文件(.txt)。然后我们根据读取来的文本信息进行编码。编码结束后,我们可以根据选择的存储路径对编码结果进行存储。解码时我们先读取出之前存储的结果。然后根据编码信息和各个字符对应的编码进行解码。最后通过界面进行展示。
    3 详细设计3.1 算法描述下图为整体程序HuffMan编解码流程图。

    我们首先从本地读取到TxT文本文件。然后将文本文件中的所有字符存进一个String对象。再对文本逐个字符进行操作,将各个字符保存并计算其出现的次数。接着对各个字符按照出现次数进行排序。然后根据排序后的字符数组进行HuffMan树的构建。接下来以左0右1的形式进行每个字符的HuffMan编码。然后根据每个字符的HuffMan编码对读取的文本文件进行编码。最后根据获得的编码以及字符与编码的对应关系进行解码。
    3.2 算法流程下图为HuffMan树创建的算法流程图。

    我首先对文本逐个字符进行操作,将各个字符保存并计算其出现的次数。然后对各个字符按照出现次数进行排序。紧接着根据排序后的字符数组进行新结点的构建。并将新结点加入List重复②③操作。最后List中只剩一个结点时为所需结点。
    4 模块实现4.1 HuffMan树的搭建算法模块创建一个Data类,用来存放每个字符。Data类包含字符的值以及其在文本中出现的次数,并且实现Comparable接口以及其比较方法,方便排序使用。
    public class Data implements Comparable<Data> { private char c = 0;//用来存放字符 private int frequency = 0;//用来存放出现的次数 //getter和setter public char getC() { return c; } public void setC(char c) { this.c = c; } public int getFrequency() { return frequency; } public void setFrequency(int frequency) { this.frequency = frequency; } //重写toString方法方便查错时输出信息 @Override public String toString() { return c + " " ; } //实现Comparable中的抽象方法 @Override public int compareTo(Data o) { //用于比较两个Data对象出现的次数 if (this.frequency < o.getFrequency()) { return -1; } else if (this.frequency > o.getFrequency()) { return 1; } else { return 0; } }}
    Node类,用来作为HuffMan树的结点结构。Node中包含一个Data对象、Node类的leftChild和rightChild,也同样实现Comparable接口,方便排序比较。
    public class Node implements Comparable<Node> { private Node leftChild = null;//左子 private Node rightChild = null;//右子 private Data data = null; //存放结点信息(Data类) //getter和setter public Data getData() { return data; } public void setData(Data data) { this.data = data; } public Node getLeftChild() { return leftChild; } public void setLeftChild(Node leftChild) { this.leftChild = leftChild; } public Node getRightChild() { return rightChild; } public void setRightChild(Node rightChild) { this.rightChild = rightChild; } //重写toString方法方便查错时输出信息 @Override public String toString() { return "Node data =" + data + "[left=" + leftChild + " , right=" + rightChild + "]"; } //实现Comparable中的抽象方法 @Override public int compareTo(Node o) { return this.data.compareTo(o.getData()); }}
    遍历从文本读来的字符串中的所有字符,将不同的字符存放到一个HashMap里,Map里的key值为字符,Value为frequency。每次读取一个字符后都在Map中查找一次,如果有那么frequency + 1否则就将字符作为key添加进去。最终得到一个char——frequency一一对应的HashMap,文本读完后,遍历HashMap,将每个Key和Value一一对应地创建为一个Data对象,其对象中的c就为该字符的值、frequency为该字符在文本中出现的次数。并且将每个Data对象都封装为一个Node对象。最后将所有得到的Node对象都添加到一个ArrayList中以便于后面HuffMan树的创建时使用。
    @Overrideprotected ArrayList<Node> string2NodeList(String string) { //Node的ArrrayList ArrayList<Node> nodeList = new ArrayList<>(); //字符和出现次数一一对应的HashMap Map<Character, Integer> codeMap = new HashMap<>(); //遍历String for (int i = 0; i < string.length(); i++) {/*对每个字符进行操作, 将不同的字符存放到一个HashMap里,Map里的key值为字符,Value为frequency。每次读取一个字符后都在Map中查找一次,如果有那么frequency+1否则就将字符作为key添加进去。*/ Character character = string.charAt(i); if (!codeMap.keySet().contains(character)) { codeMap.put(character, 1); } else { Integer oldValue = codeMap.get(character); codeMap.put(character, oldValue + 1); } } //得到一个用于遍历HashMap的set集合 Set<Character> charactersInMap = codeMap.keySet(); for (Character key : charactersInMap) {/*将每个Key和Value一一对应地创建为一个Data对象,其对象中的c就为该字符的值、frequency为该字符在文本中出现的次数。并且将每个Data对象都封装为一个Node对象。最后将所有得到的Node对象都添加到一个ArrayList中以便于后面HuffMan树的创建时使用。*/ Node node = new Node(); Data data = new Data(); data.setC(key); data.setFrequency(codeMap.get(key)); node.setData(data); nodeList.add(node); } //返回root return nodeList;}
    排序算法,用于对NodeList进行排序,方便构造HuffMan树时调用。
    //冒泡排序private void sort(ArrayList<Node> nodeList) { int size = nodeList.size(); if (size == 1) { return; } for (int i = 0; i < size; i++) { for (int j = 0; j < size - 1 - i; j++) { if(nodeList.get(j).getData().getFrequency()< nodeList.get(j + 1).getData().getFrequency()) { //交换 Node tempNode = nodeList.get(j); nodeList.set(j, nodeList.get(j + 1)); nodeList.set(j + 1, tempNode); } } }}
    开始创建HuffMan树

    将所有字符看成是一个森林(每棵树仅有一个结点)
    对包含所有结点的List进行排序
    private void init(ArrayList<Node> nodeList) { sort(nodeList); } init(nodeList);//在createTree方法中调用
    在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和
    //取出倒数两个(之前排序是从大到小,所以这两个是最小的) Node nodeLeft = nodeList.get(size - 1); Node nodeRight = nodeList.get(size - 2); //创建出新Node,数值为两个子树之和 Node nodeParent = new Node(); nodeParent.setLeftChild(nodeLeft); nodeParent.setRightChild(nodeRight); Data data = new Data(); data.setFrequency(nodeRight.getData().getFrequency() + nodeLeft.getData().getFrequency()); nodeParent.setData(data);
    从森林中删除选取的两棵树,并将新树加入森林
    //将新得到的结点加入到List中并删除掉尾部已经使用过的两个 nodeList.set(size - 2, nodeParent); nodeList.remove(size - 1);
    对得到的新List进行排序
    //调用的上面写的冒泡排序 sort(nodeList);
    如果List.size()>1,重复上面操作否则返回List中剩下的最后一个结点(为我们需要的root结点)

    4.2 界面可视化实现代码首先是第一个主界面
    public class MainActivity extends Application { private Stage start = new Stage();//最底层的stagepublic void start(Stage stage) { /*使用Grid布局格式的Pane,在Pane中使用多个Box将各组件进行“拼接”*/ Pane startScene = new GridPane(); HBox Slelection = new HBox(); VBox all = new VBox(); Text holder = new Text(" ");//用于占位 Text Welcome = new javafx.scene.text.Text(" 您好,欢迎使用本编解码软件," +"请点击按钮选择您要执行的操作"); //第一个按钮(编码按钮)以及其点击事件 Button Compress1 = new Button("编码文件"); Compress1.setOnAction(event -> { try { start.hide(); CodeActivity sceneMain = new CodeActivity(2, start); } catch (IOException e) { e.printStackTrace(); } }); //第二个按钮(解码按钮)以及其点击事件 Button DeCompress = new Button("解码"); DeCompress.setOnAction(event -> { try { start.hide(); CodeActivity sceneMain = new CodeActivity(1, start); } catch (IOException e) { e.printStackTrace(); } }); //第三个按钮(退出按钮)及其点击事件 Button Exit = new Button("退出"); Exit.setOnAction(event -> { start.close(); }); //设置各控件的大小、布局位置以及字体等属性 DeCompress.setPrefSize(80, 30); Compress1.setPrefSize(80, 30); Exit.setPrefSize(80, 30); Welcome.setFont(Font.font("宋体", FontWeight.BOLD, FontPosture.REGULAR, 20)); Welcome.setTextAlignment(TextAlignment.CENTER); holder.setFont(Font.font(5)); Slelection.getChildren().addAll(Compress1, DeCompress, Exit); Slelection.setSpacing(60); Slelection.setAlignment(Pos.CENTER); //将子控件添加到主布局里all.getChildren().addAll(holder,Welcome, Slelection); all.setPrefSize(620, 400); all.setSpacing(150); startScene.getChildren().add(all); Scene scene = new Scene(startScene, 620, 400); start.setScene(scene); start.setTitle("HuffMan编解码"); start.show(); }}

    第二个界面(第二个界面分为两个“样子”,一个是EnCode和Decode)
    public class CodeActivity { //界面子控件初始化 private FilePath mFilePath; private HuffManTest huffManTest = new HuffManTest(); private CodeAndMap codeAndMap; private Stage main_layout = new Stage(); private Button startButton = new Button("开始"); private Button firstFileButton = new Button("..."); private Button secondFileButton = new Button("..."); private Button exitButton = new Button("退出"); private Button backButton = new Button("返回首页"); private Text firstFileText; private Text secondFileText; private TextField firstTextField = new TextField();private TextField secondTextField = new TextField();//按钮点击事件CodeActivity(int selection, Stage begin) throws IOException { //退出按钮的点击事件 exitButton.setOnAction(event -> { main_layout.close(); begin.close(); }); //返回按钮的点击事件 backButton.setOnAction(event -> { main_layout.close(); begin.show(); });//在打开第二个界面的时候会根据点击的按钮传入不同的selection//selection==2为编码,即加载出编码的界面 if (selection == 2) { //浏览文件的第二个按钮 secondFileButton.setOnAction(event -> { JFileChooser fileChooser = new JFileChooser(); //设置为只能选择一个 fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int flag = fileChooser.showOpenDialog(fileChooser); if (flag == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); /*getAbsolutePath()返回的是user.dir+getPath()的内容,如:D:\\test\\test.txt*/ secondTextField.setText(file.getAbsolutePath()); } }); //文字 this.firstFileText = new Text("请选择要编码的文件"); this.secondFileText = new Text("请选择输出目录"); //开始按钮点击事件 startButton.setOnAction(event -> { inputFileName = firstTextField.getText().toString(); String outputCodeFileName = secondTextField.getText().toString() + "\\\\" + IOTools.getFileName(firstTextField.getText() .toString()) + ".dat"; String outputMapFileName = secondTextField.getText().toString() + "\\\\" +IOTools.getFileName(firstTextField.getText().toString()) + "Map.dat"; String readTxt = ""; //得到待解码文件地址 mFilePath = new FilePath(inputFileName, outputCodeFileName, outputMapFileName); try { //读取待解码文件的文本 readTxt= IOTools.getTxt(mFilePath.getInputTxtFileName()); //得到解码后的目标类对象(内包含了解码结果和Map) codeAndMap = huffManTest.encode(readTxt); //将得到的结果输出到本地进行保存 IOTools.writeCodeToDat(codeAndMap.getEncodeString(), mFilePath.getOutputCodeFileName()); IOTools.writeMap(codeAndMap.getEncodeMap(), mFilePath.getOutputMapFileName()); } catch (IOException e) { e.printStackTrace(); } //弹出成功的提示 JOptionPane.showMessageDialog(null, "OK", "编码成功", JOptionPane.PLAIN_MESSAGE); }); //浏览文件夹的按钮 firstFileButton.setOnAction(event -> { JFileChooser fileChooser = new JFileChooser(); int flag = fileChooser.showOpenDialog(fileChooser); if (flag == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); firstTextField.setText(file.getAbsolutePath()); } }); } else if (selection == 1) { //selection==1时为解码的界面 //浏览文件的第二个按钮 secondFileButton.setOnAction(event -> { //JFileChooser可以用来浏览文件夹 JFileChooser fileChooser = new JFileChooser(); //只能选择一个 int flag = fileChooser.showOpenDialog(fileChooser); if (flag == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); /*getAbsolutePath()返回的是user.dir+getPath()的内容,如:D:\\test\\test.txt*/ secondTextField.setText(file.getAbsolutePath()); } }); //设置文字 this.firstFileText = new Text("请选择要解码的文件"); this.secondFileText = new Text("请选择对应的Map文件"); //start按钮 startButton.setOnAction(event -> { //获得目标文件地址 String inputFileName = firstTextField.getText().toString(); String inputMapFileName = secondTextField.getText().toString(); try { //从地址得到Map和Code,并通过解码得到一个编解码结果对象 CodeAndMap codeAndMap = new CodeAndMap(IOTools.readCodeFromDat(inputFileName) , IOTools.getMap(inputMapFileName)); //根据Map对Code进行解码 String decode = huffManTest.decode(codeAndMap); //将解码结果保存至本地 IOTools.writeTxt(decode, inputFileName.split("[.]")[0] + "decodeTxt.txt"); //一下为通过窗口可视化展示解码结果 JFrame deCodeShow = new JFrame("解码的结果"); //解码结果的展示控件 JTextArea decodeText = new JTextArea(decode); //一个可滑动控件,以防止文本过长无法查看全部 JScrollPane decodeScroll = new JScrollPane(decodeText);decodeScroll.setBounds(13, 10, 350, 340); decodeScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); deCodeShow.add(decodeScroll); JFrame mapShow = new JFrame("各个字符所对应的编码"); //Map的展示控件 JTextArea mapText = new JTextArea(codeAndMap.mapToString()); JScrollPane mapScroll = new JScrollPane(mapText); mapScroll.setBounds(13, 10, 350, 340); mapScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); mapShow.add(mapScroll); mapShow.setSize(600, 400); mapShow.setLocation(900, 100); mapShow.setVisible(true); deCodeShow.setSize(600, 400); deCodeShow.setLocation(100, 100); deCodeShow.setVisible(true); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }); //文件浏览按钮点击事件 firstFileButton.setOnAction(event -> { JFileChooser fileChooser = new JFileChooser(); int flag = fileChooser.showOpenDialog(fileChooser); if (flag == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); firstTextField.setText(file.getAbsolutePath()); } });} //使用Box来装纳子控件 HBox firstHBox = new HBox(); HBox secondHBox = new HBox(); HBox thirdHBox = new HBox(); firstTextField.setPrefSize(200, 20); secondTextField.setPrefSize(200, 20); firstHBox.getChildren().addAll(firstFileText, firstTextField, firstFileButton); firstHBox.setAlignment(Pos.CENTER); firstHBox.setSpacing(25); secondHBox.getChildren().addAll(secondFileText, secondTextField, secondFileButton); secondHBox.setAlignment(Pos.CENTER); secondHBox.setSpacing(30); thirdHBox.getChildren().addAll(startButton, exitButton, backButton); thirdHBox.setAlignment(Pos.CENTER); thirdHBox.setSpacing(100); exitButton.setPrefSize(80, 30); startButton.setPrefSize(80, 30); backButton.setPrefSize(80, 30); VBox all = new VBox(); //将装了子控件的Box放进一个全局Box all.getChildren().addAll(firstHBox, secondHBox, thirdHBox); all.setSpacing(100); all.setAlignment(Pos.CENTER); all.setPrefSize(600, 400); Scene scene = new Scene(all, 600, 400); main_layout.setScene(scene); main_layout.setTitle("HuffMan编解码"); main_layout.show(); }}




    5 总结与体会通过这次课程设计使我们充分的理解了用哈夫曼树再编码问题中基本原理的应用,同时也学会了编写简单的哈夫曼编码问题的程序。在刚开始编程的时候,我们感到很茫然,无从下手。但是困难是可以解决的,只要通过努力,向老师虚心学习请教,再难的题目也可以自己动手完成。
    由于本次课设中用到的许多知识点都没有学过都要靠自己到课外的资料中去查找。在用的时候难免出现这样那样的错误。
    回顾起此次课程设计,至今感慨颇多,从拿到题目到完成整个编程,从理论到实践,虽然只有几天,但可以学到很多的东西,不仅可以巩固了以前所学过的知识,而且学到了很多在书本上所没有学到过的知识。通过这次课程设计使我们懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,才能真正为社会服务,从而提高自己的实际动手能力和独立思考的能力。
    在设计的过程中,难免会遇到过各种各样的问题,同时在设计的过程中发现了自己的不足之处,对以前所学过的知识理解得不够深刻,掌握得不够牢固,比如说结构体。通过这次的数据结构课程设计,我们也深刻了解到这门学问的博大精深,要积极进取,不断学习,不断积累知识。同时也认识到自己的不足和缺点,做什么事都需要细心和耐心,并坚持下去,这样才还有一个比较满意的成果。
    1 评论 17 下载 2018-10-31 15:57:22 下载需要12点积分
  • 摩根项目-股票交易模拟

    注:该项目使用的 IDE 是 Visual Studio 2010,采用通讯库为 WinSock2.h,如果使用 Linux 系统进行检测则会编译失败,使用其他 IDE 有可能发生未知异常,所以我将自己电脑上运行的一部分运行状况截图。
    1 服务器端与客户端的关系架构1.1 服务端程序的主要部分,即为项目“服务器”,采用 Windows Socket 借口,使用虚拟地址“127.0.0.1”,利用一个 while 循环来实现与多个客户端连接,每次循环连接成功后都会对当前客户端开启一个新的线程,所有客户端连接成功的 socket 都储存在全局变量 m_Server 数组里。
    1.2 普通客户端用户部分,即为项目“普通客户端”,普通客户端开启两个线程,一个用来发送订单,一个用来接受订单。用户开启客户端会有指令来操作一系列交易。客户端与服务器通过 recv 函数与 send 函数来互相发送 FIX 协议信息。
    1.3 Monitor 客户端MonitorClient 用来监测每个连接的用户进行的一系列操作。
    1.4 随机客户端RandomClient 用来模拟实际的交易所,该客户端会随机发送买卖订单给服务器。
    2 主要功能类2.1 Stock 类(股票)股票类有以下几个属性:

    averagePrice(当前价格)
    start_price(开盘价格)
    id(股票代码)
    stock_name(股票名称)
    end_price(闭盘价格)
    max_price(最高价格)
    min_price(最低价格)

    成员函数:

    每个属性对应的获取接口
    updatePrice(更新最高价格与最低价格)

    2.2 Order 类(订单)关于 Order 对象有以下几个属性:

    Order_id(每个订单独有,由系统自动生成)
    stock_name 股票名称
    amount (交易数量)
    side(买方卖方)
    avg_price(当前平均交易价格)
    give_price(订单产生时出价)
    execute_amount(已经交易的数量)
    infomation(具体的交易情况)

    成员函数:

    各个属性的获取接口
    writeResult(string opposite_name, int ex_amount, double ex_price) 该函数作用是将交易双方的一系列交易信息(交易数量、价格)写入对应的 股票 record.txt 中

    2.3 FixMessage 类(消息类)关于 FixMessage 类有以下属性:

    side(买方卖方,tag=“54”)
    average_price(平均价格,tag=“6”)
    price(价格,tag=“44”)
    filled_amount(已经交易数量,tag=“14”)
    order_id(订单号,tag=“11”)
    user_name(用户名,tag=“49”)
    open_amount(剩余未交易数量,tag=“151”)
    amount(总数,tag=“38”)
    stock_name(股票名称,tag=“1”)
    order_type(订单消息种类(新订单、取消、拒绝),tag=“35”)
    execution type(执行种类,tag=“150”)
    order_status(订单状态(新订单,部分交易,全部交易,取消), tag=“39”)

    成员函数:

    各个属性的获取接口
    在构造函数中对获取的字符串进行解析,用“;”分割出各个 tag

    3 关于匹配机制在服务器端程序中构造一个子线程共享的 Order 队列,对于每一个线程提出的要求,都要去 Order 队列里进行匹配,所以 Order 队列应对子线程保持互斥。这里使用 queue_section 全局关键段来达到互斥,给 orderList“上锁”。
    下列是全局的订单队列:

    buyOrderList:买方订单,按照价格由高到低排序,出价高的优先进行交易
    SaleOrderList:卖方订单,按照价格由低到高排序,要价低的优先进行交易
    endBuyOrder:已经结束或被取消的买方订单
    endSaleOrder:已经结束或被取消的卖方订单

    Order 进入后,先辨别是买方 Order 还是卖方 Order 进行分流,其次判断价格是否在市场内,不在的话插入队列结束,在的话直接进行匹配完成交易。 匹配细节:优先匹配最令客户满意的 Order,再进行数量匹配,出现下列三种情况:

    己方订单数量大于对方订单数量,则对方订单全部交易,己方继续与队列中下一个订单进行比较,循环直到不能再交易或全部交易。不能交易时如果未交易完,则放入队列 buyOrderList 或 SaleOrderList
    己方订单数量等于对方订单数量,双方均全部交易
    己方订单数量小于对方订单数量,己方全部交易,对方部分交易不出队列,直接结束

    用户发出取消操作,检查 buyOrderList、SaleOrderList,如果发现要取消的订单 , 则 将其 放 入 endBuyOrder 或 endSaleOrder , 如 果 在 endBuyOrder 、endSaleOrder 中找到该订单,则拒绝取消操作,因为该订单已被交易完毕。
    设置一个用户名密码的文件,根据是否存在来判定登陆成功、设置一个监视管 理账号用户名为“MonitorClient”,当服务器识别出这个用户时,会将生成的 Socket,储存在全局变量 monitor_client,登录后的交互界面如下图所示。

    从 share_information 中逐个读取股票信息,并储存在全局变量 share_map 中。

    每当有一个操作发生,如果上文提到的全局变量 monitor_client 存在,即已开启 Monitor Client,则会向该 Monitor 发送交易信息,Monitor 每接到一笔交易信息,就会将其解析判断转化为可读交易信息打印。

    关于交易的一系列规范限制

    交易价格不能超过该股票参考价格的 120%(max_price),不能低于参考价格的 80%(min_price)
    交易时间必须在系统时间上午九点半与十一点半之间,如果不在则不能发送信息,如果需要发送信息请将 Client line43-line47&&line71-line75 注释掉


    买卖股票

    取消订单

    查询现有订单
    1 评论 8 下载 2018-10-31 15:44:07 下载需要9点积分
  • 基于HTML5和JS实现的在线电子钢琴网页版

    1 引言1.1 项目背景为了体验软件工程各阶段的主要工作,熟悉项目开发流程,在HTML5标准发布已久的背景下,基于HTML5增加的新特性,设计一个网页版的电子钢琴,来实现上述目的。
    1.2 目的该需求规格说明书是针对本次软件工程课程设计编写,编写需求规格说明书的目的是为了对未来网站的需求做一个规范的描述。
    该需求规格说明书对功能需求、性能需求和其它非功能性需求进行了详细的描述,详尽说明了这一软件产品的需求和规格,这些规格说明是进行设计的基础,也是编写测试用例和进行系统测试的主要依据。同时,该文档也是用户确定软件功能需求的主要依据。
    1.3 读者对象本需求规格说明书的读者对象为该网站的开发人员,课设验收老师。
    1.4 定义1.4.1 HTML5(HyperText Markuplanguage5)万维网的核心语言、标准通用标记语言下的一个应用超文本标记语言(HTML)的第五重大修改。
    1.4.2 CSS它是一种用来表现HTML或XML等文件样式的计算机语言。CSS目前最新版本为CSS3,是能够真正做到网页表现与内容分离的一种样式设计语言。
    1.4.3 Jsjs即 Javascript ,是一种由Netscape的LiveScript发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
    1.5 项目环境
    开发语言:HTML5+CSS3+JavaScript。
    运行环境:IE 9以上、Firefox 10以上、Chrome 17以上

    2 需求分析2.1 基本构造
    可供点击的8个独立白键,5个黑键。能够使用特定键盘按键点击,并且钢琴上的键可对应键盘上多个按键
    操作对应琴键时,显示动画效果,并伴有音效

    2.2 需求概述本项目旨在开发一个基于网页的模拟钢琴,以供用户平时休闲娱乐使用。本系统采用B/S模式,联合运用HTML5、CSS3和JavaScript多种典型网页设计技术完成系统,并且兼容了当前市场上主流的大多数浏览器。
    用户打开网页后,可以直接得到模拟钢琴,并可以使用键盘进行弹奏,也可使用鼠标点击相应模拟琴键进行弹奏。
    基于网页的模拟钢琴主要需求如下:

    键盘弹奏功能:用相应的键盘按键模拟钢琴黑白键,点击按键,相应的模拟琴键颜色变化并发出相应的音调,然后琴键颜色恢复
    鼠标弹奏功能:用鼠标点击网页中模拟琴键,模拟琴键颜色变深并发出相应的音调,然后琴键颜色恢复

    2.3 整体细节
    琴键上显示键盘操作提示,白键显示在下方,黑键显示在上方
    网页下方显示操作提示
    网页使用动态背景,增加美观程度

    3 总体设计3.1 基本设计概念和处理流程用户使用流程如下图所示:

    3.2 程序结构


    文件
    文件描述




    index.html
    主程序,负责界面布局与程序框架构建


    zzsc.css
    控制网页中图片,文字等的样式


    zzsc.js
    主要用于实现应用逻辑,包括准备、设置声音文件,处理鼠标键盘点击事件等



    3.3 功能分配


    模块
    子模块
    说明




    界面设计
    背景动画
    能够使用比较清新的动态背景,利用HTML5的video新标签完成



    钢琴界面
    使用黑键和白键组成传统意义上的钢琴



    文字提示
    钢琴键盘上提示对应键盘的按键;钢琴下方提示浏览器版本和使用钢琴方法


    音频输出
    键盘输出
    获取键盘的按键键值,输出对应的声音



    鼠标点击输出
    获取点击的位置即钢琴的按键来输出对应的声音


    动画设计
    点击钢琴后的改变
    每次点击钢琴的键后键盘的颜色都会有所改变,增强和用户的交互感



    4 开发工作评价4.1 对生产效率的评价网页钢琴从整体来说,能够满足预期的全部需求,并且在测试及运行过程中,并未发现任何重大问题和漏洞。在文档的生产效率上,历时两天生产处4份文档,日均两篇。
    4.2 对产品质量的评价钢琴的基本操作均已经实现,可以正常运行。网页细节也进行了优化,界面美观,用户体验好。
    4.3 对技术方法的评价使用分布式的版本控制系统Git,使成员更真实的体验了软件项目迭代、更替的过程。项目初期由于对Git使用方法不熟悉对项目进度造成了一些阻碍,熟悉以后明显对代码提交项目跟进明显加速。
    4.4 出错原因的分析主要有以下几个方面的原因需要我们可以以后注意:

    对软件开发的流程不是很熟悉。因为这方面知识的获取只是停留在理论层面,缺乏实际经验
    组员之间的交流还有待提高
    web开发技术不够纯熟

    5 经验和教训通过本次课程设计。使我们对HTML语言的了解更加深入了,这有利于于我们以后的发展。不过我们也得到了一些教训,就是软件开发过程中“设计”是非常重要的,要想开发出好的作品,与一个好的设计是分不开的。
    经过这段时间开发过程组内成员了解了软件工程序的具体涵义,熟悉了开发流程,也掌握了软件文档的编写标准,将开发流程完整经历了一次,但是由于初次开发,对项目时间安排可能存在规划不科学等问题但是最终成功完成了整个项目,在这过程中学到的知识很值得珍惜。
    6 界面截图网站程序主界面

    按键按下1

    按键按下2
    1 评论 16 下载 2018-10-31 15:24:29 下载需要9点积分
  • 基于Cocos2d-x实现的星际冒险游戏

    1 开发环境
    使用语言:C++
    开发平台:VS2013 update4
    适用操作系统:Windows 8

    2 游戏介绍2.1 创意来源
    著名科幻小说《三体》所描述的浩瀚宇宙背景;
    电影《星际穿越》中的恢弘宇宙飞行场景;

    2.2 故事背景三体星忌惮人类,派军队封锁地球,人类无法抵御强大的三体远征军,为了文明的延续,必须远赴外太空寻找出路。而只有穿越虫洞(第一张地图有两个)才能在广袤宇宙中进行星际航行,寻找适合人类移民的星球(第二张地图中)。
    2.3 游戏规则介绍本作为Advemture的首代版本,凭借引人入胜的剧情,神秘奇幻的背景音乐,多样有趣的道具设计成为不可多得的精品游戏。游戏共有两种场景,在两个场景中,玩家均可使用“WASD”按键操纵飞船移动,场景一为非实时场景,玩家需要选择合适路线前往地图出口“虫洞”,路途中将有怪兽、黑洞、随机事件等物件给玩家的旅途增添乐趣;在场景一中触碰黑洞将进入场景二,场景二为即时操作场景,玩家需在黑洞中操作飞船躲避陨石撞击,达到指定时间后,将会退出场景二,在场景一题地图上另一随机黑洞处出现。玩家最终到达宜居星球时即可胜利。游戏过程中,当飞船生命值下降为0时,游戏结束。
    2.4 游戏图标介绍2.4.1 玩家飞船
    玩家控制该飞船探索人类文明延续之路。(用键盘wasd表示上下左右)
    等级 生命值 攻击力 所需经验等级0 50 10 50等级1 70 15 100等级2 90 20 150等级3 110 25 200等级4 130 30 250等级5 150 35 300……
    每次升级时【生命值】达到该等级满值,游戏左侧属性栏标明玩家当前各项属性。
    2.4.2 星际陨石
    每次减少飞船100点血量。玩家需要尽量避开它。
    2.4.3 太空站
    补给场所,每次增加飞船到当前等级的满血量。
    2.4.4 星级怪兽1
    血量50,攻击力10,胜利获得经验值50。
    2.4.5 星级怪兽2
    血量100,攻击力20,胜利获得经验值75。
    2.4.6 星级怪兽3
    星际怪兽3:血量150,攻击力30,胜利获得经验值150。
    2.4.7 随机事件
    触碰后触发随机事件。可能有:

    内部成员背叛,血量减少60
    拾获太空水晶,攻击力增加5
    遭遇黑洞,进入另一场景

    2.4.8 黑洞
    触碰后进入另一场景。玩家需控制飞船躲开黑洞中来回碰撞的陨石、能把飞船吸入的空间。游戏时间达到15秒(具体时间可改变)时游戏胜利。若胜利,,经验增加50,同时随机从另一个黑洞出现(游戏不确定性之处,其中一个虫洞被黑洞包围,若想从这里通过需要一定运气;若失败,则飞船被黑洞摧毁,游戏结束。(这里的一个攻略就是,当血量或等级不够时,打不过那些怪物,就需要进入黑洞获得经验来升级,但也必须迎接黑洞中的挑战)。
    2.4.9 虫洞
    当前地图出口,到达后当前关卡胜利。飞船穿越当前星际,到达虫洞,再进入更远的太空进行探索。
    2.4.10 宜居星球
    在第二张地图中,是游戏的终点。到达后显示胜利。
    2.4.11 炸弹道具
    触碰后可将屏幕中的陨石炸毁,用q键释放。
    2.4.12 护甲道具
    触碰后可在5秒内不收陨石伤害(用e键释放)。
    2.4.13 太空水晶道具
    具有魔力,可以增加一次瞬移机会(点击鼠标触发,点哪里移动到哪里)。
    2.4.14 人物状态栏
    在场景一中出现于界面左侧,显示任务当前生命值、攻击力、经验值、等级等。
    2.4.15 血条
    在场景二黑洞中出现于界面正上方,长度和左侧数值共同代表人物剩余生命值,长度减为0或左侧数值为0时,飞船被摧毁,游戏结束。
    2.4.16 时间进度条
    在场景二黑洞中出现于界面右上方,显示飞船还需要在黑洞中坚持的时间,数值减为0时,胜利逃出黑洞。
    2.5 游戏细节展示本作虽然只是Advemture的首代版本,但做工非常精良,在各种细节处理方面可见一斑。比如陨石、道具在撞击到屏幕边框时会产生镜面反弹,撞击到飞船时,对飞船的运动情况会产生相应的影响(表现为飞船带有撞击陨石的速度,且短时间内移动操作困难)。
    3 游戏展示3.1 游戏页面3.1.1 主页面
    3.1.2 游戏背景介绍
    3.1.3 游戏页面场景一
    3.1.4 游戏页面场景二
    3.1.5 游戏页面场景二(2)
    4 游戏攻略由于在一些设定下游戏难度较大,因此提供一些攻略说明:

    场景1界面跳转到黑洞界面-不同切换效果预示着黑洞中有不同的效果(攻略:可以根据切换效果,预知黑洞中的效果)
    在黑洞中会有各种效果,被陨石撞到会影响飞船的飞行,需要按方向键重新调整飞船飞行(攻略:快速按方向键,而不是按着方向键)

    善用道具,场景太混乱时使用炸弹道具,使用护甲道具后因不怕撞击,应尽可能趁机会收集其余道具。
    1 评论 2 下载 2018-10-31 15:18:42 下载需要3点积分
  • 基于VC++的MFC框架实现的数字图像处理算法

    一、课程背景数字图像处理技术的发展涉及信息科学、计算机科学、数学、物理学以及生物学等学科,因此数理及相关的边缘学科对图像处理科学的发展有越来越大的影响。近年来,数字图像处理技术日趋成熟,它广泛应用于空间探测、遥感、生物医学、人工智能以及工业检测等许多领域,并促使这些学科产生了新的发展。
    图像是人类获取和交换信息的主要来源,因此,图像处理的应用领域必然涉及到人类生活和工作的方方面面。随着科学技术的发展,数字图像处理技术的应用领域也将随之不断扩大。
    正因为图像处理在社会生活中有着越来越重要的作用,这也就体现了我们学习这门课程的必要性。而通过课程的学习可以让我们掌握一些图像处理的初步技术,从而为今后在图像处理的学习方面打下了基础。
    二、课程选题由于老师并没有做出规定去做哪一道题目,这给了我们很大程度上的自主性。我根据自身的情况和能力,选择了经典的几种边缘检测算子的特点的比较这个题目,和图象的平移和镜像,又因为在计算机图形学这门课中有讲到图片相加的具体程序,在这里图简单做一下颜色取反。通过对这些算法的编写,更加深入地了解了图像处理的过程,并且也在一定程度上提高了自己的编程能力。
    边缘检测技术是所有基于边界分割的图像分析方法的第一步,首先检测出图像局部特性的不连续性,再将它们连成边界,这些边界把图像分成不同的区域,检测出边缘的图像就可以进行特征提取和形状分析。为了得到较好的边缘效果,现在已经有了很多的边缘检测算法以及一些边缘检测算子的改进算法。但各算子有自己的优缺点和适用领域。基于以上理由,在此对学到的一些一些经典边缘检测算子进行实际编程验证,以便实际应用中更好地发挥其长处。
    三、设计思想拉普拉斯算子
    拉普拉斯算子是二阶微分算子,其差分的表示形式如下:

    拉普拉斯边缘检测算子的模板如下图所示,模板的基本特征是中心位置的系数为正,其余位置的系数为负,且模板的系数之和为零。它的使用方法是用图中的两个点阵之一作为卷积核,与原图像进行卷积运算即可。



    0
    -1
    0




    -1
    4
    -1


    0
    -1
    0






    -1
    -1
    -1




    -1
    8
    -1


    -1
    -1
    -1



    索贝尔算子
    该算子是由两个卷积核g1(x, y)与g2(x, y)对原图像f(x, y)进行卷积运算而得到的。用差分代替一阶偏导,算子的计算方法如下:

    Sobel算子垂直方向和水平方向的模板如下图所示,前者可以检测出图像中的水平方向的边缘,后者则可以检测图像中垂直方向的边缘。实际应用中,图像中的每一个像素点都用这两个卷积核进行卷积运算,取其最大值作为输出。运算结果是一幅体现边缘幅度的图像。



    -1
    -2
    -1




    0
    0
    0


    1
    2
    1






    -1
    0
    1




    -2
    0
    2


    -3
    0
    1



    罗伯特(Robert)算子
    Robert梯度算子所采用的是对角方向相邻两像素值之差,所以用差分代替一阶偏导,算子形式可表示如下:

    上述算子对应的两个2x2模板如下图所示。实际应用中,图像中的每个像素点都用这两个模板进行卷积运算,为避免出现负值,在边缘检测时常提取其绝对值。



    1
    0




    0
    -1






    0
    1




    -1
    0



    prewwit(普瑞维特)算子
    用差分代替一阶偏导可得算子形式如下:

    Prewitt边缘检测算子的两个模板如下图所示,它的使用方法同Sobel算子一样,图像中的每个点都用这两个核进行卷积,取得最大值作为输出。Prewitt算子也产生一幅边缘图像。



    -1
    -1
    -1




    0
    0
    0


    1
    1
    1






    1
    0
    -1




    1
    0
    -1


    1
    0
    -1



    镜像,平移,颜色取反
    都是对原图像(矩阵)进行遍历。
    四、数据结构与算法设计拉普拉斯算子
    很明显,算子用矩阵表示,在c++里面是二维数组,通过对话框确定使用哪个运算模板,通过调用模板运算函数实现。

    具体的伪代码如下:
    获得doc类的指针对象,因为指向位图数据的句柄这个成员变量在doc类里面;得到指向图像数据开始(不包含文件头)的指针,并且锁住图像所占用的那块内存;判断是否是8位的bmp图片,如果不是,报错返回,如果是则继续弹出选择具体模板的对话框,if(选择了模板,并且按下了确定按钮){ 调用doc类里面dib类对象的模板运算函数; if(函数模板运算成功) { 图像设置脏标记; 迫使程序重绘 } else 报错,返回 }
    因为下面几个算子的情况大同小异,下面不再详述!
    模板运算函数
    模板卷积在空域实现的主要步骤如下:

    将模板在图中漫游,并将模板中心与图中某个像素位置重合
    将模板上的各个系数与模板下各对应像 素的灰度值相乘
    将所有乘积相加(为保持灰度范围,常将结果再除以模板的系数个数)
    将上述运算结果(模板的输出响应)赋 给图中对应模板中心位置的像素

    具体的伪码如下:
    得到打开的图像的信息,计算每行的字节数;通过已经打开的图像的信息,新申请一块内存,并将打开的图像数据复制进来, 得到指向新申请的内存的指针,锁定指针所指向的内存;for (i = iTempMY; i < lHeight - 2*iTempMY; i++) // 行(除去边缘几行){ for (j = iTempMX; j < lWidth - 2*iTempMX; j++)// 列(除去边缘几列) { // 指向新DIB第i行,第j个象素的指针 pTemp = (unsigned char*)lpNewDIBBits + byteofLine * (lHeight - 1 - i) + j;// 保存当前像素点的灰度值 result = 0; for (int k = 0; k < iTempH; k++) { for (int l = 0; l < iTempW; l++) { // 指向DIB第i - iTempMY + k行,第j - iTempMX + l个象素的指针 lpSrc = (unsigned char*)lpDIBBits + byteofLine * (lHeight - 1 - (i - iTempMY + k)) + (j - iTempMX + l); // 保存象素值 result += (*lpSrc) * fpArray[k * iTempW + l]; } } result *= fCoef; // 乘上系数 result = (FLOAT)fabs(result); // 取绝对值 if (result > 255) //因为是256色图,超过直接赋值为255 { *pTemp = 255; } else { *pTemp = (unsigned char)(result + 0.5);//四舍五入取整 } } } 将新开辟的内存里面的数据复制给以前的打开的图像所占的内存; 为新开辟的内存解锁; 释放掉新开辟的内存; return TRUE;}
    索贝尔(sobel)算子

    对原图像进行两次模板运算,然后取两次模板运算的值中最大值作为新的图像的灰度值
    罗伯特(Robert)算子

    在此没有调用模板运算的函数,直接进行对角元素相减
    Prewwit算子

    除了模板和sobel算子不一样,其余的全都一样
    平移、取反、镜像

    平移
    新申请一块内存,初始化为已经打开的图像平移是用两个for循环,遍历临时图像的每个点如果临时图像的点平移回去没有出界,则保留,否则,直接赋为255(出界的点直接剪切)将新申请的内存的内容复制到打开的图像所在的内存重绘


    取反
    新申请一块内存,初始化为已经打开的图像;平移是用两个for循环,遍历临时图像的每个点,每个点的值=255-点的值;将新申请的内存的内容复制到打开的图像所在的内存;重绘


    镜像
    垂直镜像的话,交换第一行的像素和倒数第一行的像素,交换第二行的像素和倒数第二行的像素;水平镜像同样的道理

    五、运行结果与分析5.1 程序运行结果


    原始图片
    有噪声的原始图片









    拉普拉斯算子



    中心为4的拉普拉斯
    中心为8








    中心为4的
    中心为8的







    罗伯特(Robert)算子



    效果1
    效果2









    Prewwit算子



    效果1
    效果2









    平移、取反、镜像



    平移
    取反








    垂直镜像
    水平镜像







    5.2 分析
    图三四,和图六、八、十相比,很明显的得出结论,拉普拉斯算子对噪声敏感,在应用过程中应注意先进行平滑预处理
    图三、四、六、八、十纵向对比,可见Robert算子对噪声的抵抗能力最好,sobel和prewwit算子次之
    比较图五六和图九十可见sobel算子和Prewwit算子几乎一样

    Sobel算子模板



    -1
    -2
    -1




    0
    0
    0


    1
    2
    1






    -1
    0
    1




    -2
    0
    2


    -3
    0
    1



    Prewitt算子模板



    -1
    -1
    -1




    0
    0
    0


    1
    1
    1






    1
    0
    -1




    1
    0
    -1


    1
    0
    -1




    这几个算子中只有拉普拉斯算子是二阶算子,其余的均是一阶算子。但是也数拉普拉斯算子丢失边的信息最为严重。纵向对比图九七五二一,图一、二,也就是拉普拉斯算子的那两幅图,丢失lena身后的柱子信息(图最左面)最为严重,图七(Robert算子处理的那幅图)次之,剩余的保留信息最好。观察帽子顶的信息,图一和图七最差,图二次之,图五和图九最好
    很容易看出,sobel算子和prewwit算子检测出的边最为厚,尤其是sobel算子,Robert算子检测出的边最细

    目前,边缘检测技术在很多领域中都得到了广泛的应用。如对医学图像的边缘特征提取、车牌识别、人脸检测技术等。本文比较了几种常用的经典边缘检测算子。可以看出它们都不是具有绝对优势的方法,都各自存在优缺点。所以对于图像的边缘检测,要根据不同的系统,针对不同的环境条件和要求,选择合适的算子。
    以上是我从自己处理的图像中所看出来的,并没有严谨的理论分析,实际测试的样本数量也不是很多,仅仅作为验证,结果也基本符合网上的别人的总结,但还是有出入的。通过对这几种算子的比较,加深了对这几种算子的印象,在以后的学习和工作中能比较准确的判断使用哪种算子,为将来的学习工作打下了基础。


    注:红线的地方是结论基本一致的地方,画框是出入很大的地方,没划线的是我这里是没有结论的。
    六、困难及解决方法6.1 自己的结果产生错误自己历尽千辛万苦好不容易出来一幅图片,没有产生中断而崩掉,激动之后,自己做的结果到底对不对啊?和同学做的结果对照一下,很容易发现不一样:



    效果1
    效果2









    (这两幅图片都是用prewitt算子处理的,sobel也有这样的问题)
    后来发现是有符号的char和无符号的char的问题。

    百度之后,问题解决了。
    6.2 MFC编程过程中的问题很明显,在做课程设计的时候要用到内存的开辟,复制,释放等等。
    在做这个的过程中,学到了一个新的函数memcpy。

    但是在用的过程中很容易出现崩掉的情况,碰到这种情况之后只能自己检查数组在哪个地方越界。
    还有一些“灵异事件”,程序崩了,怎么看怎么对,调试也没调出来,新建一个工程,重新拷贝一份,又好了。
    6.3 选题由于是自己选题,所以想找个简单点的,所以在空域里面做,选了上面的几个题目,课程里面涉及到的边缘检测算子里面还有两个,一个是马尔算子,用到了平滑,另一个是Canny算子,要用到高斯滤波器,所以这两个算子没做。其实我也想在频域里面做一下,感受一下高通滤波的效果,但是让傅里叶变换就给治住了,所以就放弃了。
    6.4 对这几个算子的理解很明显从上面可以看出,我对这几个算子的理解还是很肤浅的,直接把它们的离散化的形式拿过来用,直接把卷积模板套用一下。我还是希望有机会,能推导一个算子的公式,加深一下理解。但是,通过自己在编码过程中的思考以及在代码中调试,也同样加深了自己对于该方法的理解,并且在程序中得到了很好地实现,收获还是很多的。
    6.5 查资料很明显,去网上可以搜到很多的关于这方面的资料,但是有的是论文,看起来很难,有的呢是比较垃圾的。关键就靠我们自己,擦亮眼睛,去找难度适合的能满足我们需求的资料。
    七、总结心得通过一学期的课程学习我们虽说还没有完全掌握数字图像处理技术,但也收获了不少,对于数字图像方面的知识有了深入的了解,更加理解了数字图像的本质,即是一些数字矩阵,但灰度图像和彩色图像的矩阵形式是不同的。对于一些耳熟能详的数字图像相关术语有了明确的认识,比如常见的:像素(衡量图像的大小)、分辨率(衡量图像的清晰程度)、位图(放大后会失真)、矢量图(经过放大不会失真)等大家都能叫上口却知识模糊的名词。也了解图像处理技术中一些常用处理技术的实质,比如锐化处理是使模糊的图像变清晰,增强图像的边缘等细节。而平滑处理是的目的是消除噪声,模糊图像,在提取大目标之前去除小的细节或弥合目标间的缝隙。对常提的RGB图像和灰度图像有了明确的理解,这对大家以后应用PHOTOSHOP等图像处理软件对图像进行处理打下了坚实的基础,就像武侠小说里面的“内功”。更重要的是了解到了数字图像处理的思想。通过学习也是对C++编程应用的很好的实践与复习。
    当然通过几周的的课程学习还是远远不够的,也有许多同学收获甚微,我总结了下大家后期的学习态度与前期的学习热情相差很大的原因。刚开始大家是有很高的热情学习这门课的,可是随着课程的逐渐深入学习,大家渐渐发现课程讲授内容与自己起初想学的实用图像处理技术是有很大的差别的,大家更着眼于如何利用一些软件、技术去处理图像而得到满意的效果,或者进行一些图像的创意设计,可是课程的内容更偏重于如何通过编程实现实现如何对图像进行一些类似于锐化、边缘提取、模糊、去除噪声等基础功能的实现,这其中涉及很多算法、函数,需要扎实的数学基础和编程基础,并且需要利用大量时间在课下编写代码,并用VISUAL C++软件实现并进行调试,然而大部分人的C++实践能力以及编程能力还有待提高,尤其是对于矩阵进行操作的编程尤为是个考验,并且后半学期课程任务较重,时间不是很充裕,这对于需要大量实践的数字图像处理课程就是个很大的问题。
    在老师授课方面建议可以在课上多进行具体操作,这样可以提起大家学习的兴趣,也可以让大家在课下积极准备,然后在上课由学员进行演示,还可以加入一些数字图像处理的经典范例,加深同学们的学习热情。
    八、程序操作说明及主要代码8.1 程序操作的简要说明书打开一幅图片


    从菜单选取操作



    如果要放弃所做的操作


    如果要保存所做的操作

    8.2 主要代码//拉普拉斯算子菜单的响应函数void CmyDibView::OnLplsSharp(){ CmyDibDoc* pDoc = GetDocument(); LPSTR lpDIB; // 指向DIB的指针 LPSTR lpDIBBits; // 指向DIB象素指针 int iTempH=3; // 模板高度 int iTempW=3; // 模板宽度 float fTempC=1; // 模板系数 int iTempMX=1; // 模板中心元素X坐标 int iTempMY=1; // 模板中心元素Y坐标 float aValue[9]; // 模板元素数组 lpDIB = (LPSTR) ::GlobalLock((HGLOBAL)pDoc->GetHDIB()); // 找到DIB图像象素起始位置 lpDIBBits = pDoc->GetDibImage()->FindDIBData(lpDIB); // 判断是否是8-bpp位图 if (pDoc->GetDibImage()->DIBNumColors(lpDIB) != 256) { MessageBox(_T("只支持256色位图的锐化!"), _T("系统提示"), MB_ICONINFORMATION | MB_OK); ::GlobalUnlock((HGLOBAL)pDoc->GetHDIB()); return; } CLPLSDlg dlg; if (IDOK == dlg.DoModal()) { aValue[0] = dlg.m_n1; aValue[1] = dlg.m_n2; aValue[2] = dlg.m_n3; aValue[3] = dlg.m_n4; aValue[4] = dlg.m_n5; aValue[5] = dlg.m_n6; aValue[6] = dlg.m_n7; aValue[7] = dlg.m_n8; aValue[8] = dlg.m_n9; // 调用Template()函数用拉普拉斯模板锐化DIB if (pDoc->GetDibImage()->Template(lpDIBBits, pDoc->GetDibImage()->DIBWidth(lpDIB), pDoc->GetDibImage()->DIBHeight(lpDIB), iTempH, iTempW, iTempMX, iTempMY, aValue, fTempC)) { pDoc->SetModifiedFlag(TRUE); // 设置脏标记 pDoc->UpdateAllViews(NULL); // 更新视图 } else { MessageBox(_T("分配内存失败!"), _T("系统提示"), MB_ICONINFORMATION | MB_OK); } ::GlobalUnlock((HGLOBAL)pDoc->GetHDIB()); }}
    //功能:空域上的模板运算//参数:// lpDIBBits 指向源DIB图像指针 lWidth 源图像宽度(象素数)// lHeight 源图像高度(象素数) iTempH 模板的高度// iTempW 模板的宽度 iTempMX 模板的中心元素X坐标 ( < iTempW - 1)// iTempMY 模板的中心元素Y坐标 ( < iTempH - 1) fpArray 指向模板数组的指针// FLOAT fCoef 模板系数//返回值:成功返回TRUE,否则返回FALSE。bool CMyDib::Template(LPSTR lpDIBBits, long lWidth, long lHeight, int iTempH, int iTempW, int iTempMX, int iTempMY, float * fpArray, float fCoef){ LPSTR lpNewDIBBits; // 指向复制图像的指针 HLOCAL hNewDIBBits; //指向一块内存区域的指针 unsigned char* lpSrc; // 指向源图像的指针 unsigned char* pTemp; // 指向要重新赋值区域的像素点指针 float result; // 计算结果 long byteofLine; // 图像每行的字节数 byteofLine = LineBytes(lWidth * 8); // 暂时分配内存,以保存新图像 hNewDIBBits = LocalAlloc(LHND, byteofLine * lHeight); if (hNewDIBBits == NULL) { return FALSE; } lpNewDIBBits = (char *)LocalLock(hNewDIBBits); // 初始化图像为原始图像 memcpy(lpNewDIBBits, lpDIBBits, byteofLine * lHeight); for (int i = iTempMY; i < lHeight - 2*iTempMY; i++) // 行(除去边缘几行) { for (int j = iTempMX; j < lWidth - 2*iTempMX; j++)// 列(除去边缘几列) { // 指向新DIB第i行,第j个象素的指针 //lpDst = (unsigned char*)lpNewDIBBits + lLineBytes * i + j; //这样的话图像会倒置 pTemp = (unsigned char*)lpNewDIBBits + byteofLine * (lHeight - 1 - i) + j; result = 0; for (int k = 0; k < iTempH; k++) { for (int l = 0; l < iTempW; l++) { // 指向DIB第i - iTempMY + k行,第j - iTempMX + l个象素的指针 lpSrc = (unsigned char*)lpDIBBits + byteofLine * (lHeight - 1 - (i - iTempMY + k)) + (j - iTempMX + l); // 保存象素值 result += (*lpSrc) * fpArray[k * iTempW + l]; } } result *= fCoef; // 乘上系数 result = (FLOAT)fabs(result); // 取绝对值 if (result > 255) { *pTemp = 255; } else { *pTemp = (unsigned char)(result + 0.5); } } } // 复制变换后的图像 memcpy(lpDIBBits, lpNewDIBBits, byteofLine * lHeight); LocalUnlock(hNewDIBBits); LocalFree(hNewDIBBits); return TRUE;}
    //功能:该函数用来镜像DIB图像。可以指定镜像的方式是水平还是垂直。//参数:// LPSTR lpDIBBits 指向源DIB图像指针// LONG lWidth 源图像宽度(象素数)// LONG lHeight 源图像高度(象素数)// BOOL bDirection 镜像的方向,TRUE表示水平镜像,FALSE表示垂直镜像//返回值:镜像成功返回TRUE,否则返回FALSE。bool CMyDib::MirrorDIB(LPSTR lpDIBBits, LONG lWidth, LONG lHeight, BOOL bDirection){ LPSTR lpSrc; // 指向源图像的指针 LPSTR lpDst; // 指向要复制区域的指针 LPSTR lpBits; // 指向复制图像的指针 HLOCAL hBits; LONG i; // 循环变量 LONG j; LONG lLineBytes; // 图像每行的字节数 lLineBytes = LineBytes(lWidth * 8);// 计算图像每行的字节数 hBits = LocalAlloc(LHND, lLineBytes); // 暂时分配内存,以保存一行图像 if (hBits == NULL) { return FALSE; } lpBits = (char *)LocalLock(hBits); // 判断镜像方式 if (bDirection) // 水平镜像 { // 针对图像每行进行操作 for (i = 0; i < lHeight; i++) { // 针对每行图像左半部分进行操作 for (j = 0; j < lWidth / 2; j++) { // 指向倒数第i行,第j个象素的指针 lpSrc = (char *)lpDIBBits + lLineBytes * i + j; // 指向倒数第i行,倒数第j个象素的指针 lpDst = (char *)lpDIBBits + lLineBytes * (i + 1) - j; // 备份一个象素 *lpBits = *lpDst; // 将倒数第i行,第j个象素复制到倒数第i行,倒数第j个象素 *lpDst = *lpSrc; // 将倒数第i行,倒数第j个象素复制到倒数第i行,第j个象素 *lpSrc = *lpBits; } } } else // 垂直镜像 { // 针对上半图像进行操作 for (i = 0; i < lHeight / 2; i++) { // 指向倒数第i行象素起点的指针 lpSrc = (char *)lpDIBBits + lLineBytes * i; // 指向第i行象素起点的指针 lpDst = (char *)lpDIBBits + lLineBytes * (lHeight - i - 1); // 备份一行,宽度为lWidth memcpy(lpBits, lpDst, lLineBytes); // 将倒数第i行象素复制到第i行 memcpy(lpDst, lpSrc, lLineBytes); // 将第i行象素复制到倒数第i行 memcpy(lpSrc, lpBits, lLineBytes); } } LocalUnlock(hBits); LocalFree(hBits); return TRUE;}
    //功能:水平移动DIB图像。函数不会改变图像的大小,移出的部分图像// 将截去,空白部分用白色填充。//参数:lpDIBBits 指向源DIB图像指针// lWidth 源图像宽度(象素数)// lHeight - 源图像高度(象素数)// lXOffset - X轴平移量(象素数)// lYOffset - Y轴平移量(象素数)//返回值:平移成功返回TRUE,否则返回FALSE。bool CMyDib::TranslationDIB1(LPSTR lpDIBBits, long lWidth, long lHeight, long lXOffset, long lYOffset){ LPSTR lpSrc; // 指向源图像的指针 LPSTR lpDst; // 指向要复制区域的指针 LPSTR lpNewDIBBits; // 指向复制图像的指针 HLOCAL hNewDIBBits; LONG i; // 象素在新DIB中的坐标 LONG j; LONG i0; // 象素在源DIB中的坐标 LONG j0; LONG lLineBytes; // 图像每行的字节数 lLineBytes = LineBytes(lWidth * 8);// 计算图像每行的字节数 // 暂时分配内存,以保存新图像 hNewDIBBits = LocalAlloc(LHND, lLineBytes * lHeight); if (hNewDIBBits == NULL) { return FALSE; } lpNewDIBBits = (char *)LocalLock(hNewDIBBits); for (i = 0; i < lHeight; i++) // 每行 { for (j = 0; j < lWidth; j++) // 每列 { // 指向新DIB第i行,第j个象素的指针 // 注意由于DIB中图像第一行其实保存在最后一行的位置,因此lpDst // 值不是(char *)lpNewDIBBits + lLineBytes * i + j,而是 // (char *)lpNewDIBBits + lLineBytes * (lHeight - 1 - i) + j lpDst = (char *)lpNewDIBBits + lLineBytes * (lHeight - 1 - i) + j; // 计算该象素在源DIB中的坐标 i0 = i - lXOffset; j0 = j - lYOffset; /*i0 = i; j0 = j;*/ // 判断是否在源图范围内 if ((j0 >= 0) && (j0 < lWidth) && (i0 >= 0) && (i0 < lHeight)) { // 指向源DIB第i0行,第j0个象素的指针 // 同样要注意DIB上下倒置的问题 lpSrc = (char *)lpDIBBits + lLineBytes * (lHeight - 1 - i0) + j0; *lpDst = *lpSrc; // 复制象素 } else { //对于源图中没有的象素,直接赋值为255 *((unsigned char*)lpDst) = 255; } } } // 复制平移后的图像 memcpy(lpDIBBits, lpNewDIBBits, lLineBytes * lHeight); LocalUnlock(hNewDIBBits); LocalFree(hNewDIBBits); return TRUE;} //取反菜单的响应函数void CmyDibView::OnAddcolor(){ CmyDibDoc* pDoc = GetDocument(); LPSTR lpDIB; // 指向DIB的指针 LPSTR lpDIBBits; // 指向DIB象素指针 lpDIB = (LPSTR) ::GlobalLock((HGLOBAL)pDoc->GetHDIB()); // 找到DIB图像象素起始位置 lpDIBBits = pDoc->GetDibImage()->FindDIBData(lpDIB); int imgWidth = pDoc->GetDibImage()->DIBWidth(lpDIB); int ImgHeight = pDoc->GetDibImage()->DIBHeight(lpDIB); int byteofLine = LineBytes(imgWidth * 8); LPSTR lpNewDIBBits; // 指向复制图像的指针 HLOCAL hNewDIBBits; //指向一块内存区域的指针 // 暂时分配内存,以保存新图像 hNewDIBBits = LocalAlloc(LHND, byteofLine * ImgHeight); if (hNewDIBBits == NULL) { return ; } lpNewDIBBits = (char *)LocalLock(hNewDIBBits); // 初始化图像为原始图像 memcpy(lpNewDIBBits, lpDIBBits, byteofLine * ImgHeight); for (int y = 0; y<ImgHeight; y++) { for (int x = 0; x<imgWidth; x++) { lpNewDIBBits[y*imgWidth + x] = 255 - lpNewDIBBits[y*imgWidth + x]; } } memcpy(lpDIBBits, lpNewDIBBits, byteofLine * ImgHeight); LocalUnlock(hNewDIBBits); LocalFree(hNewDIBBits); Invalidate();}
    1 评论 6 下载 2018-10-31 15:04:10 下载需要15点积分
  • 基于Python的PyGame实现的横板动作小游戏

    游戏玩法玩家操纵主角进行移动和攻击,主角有四种攻击,不同操作惊醒不同的攻击和动画播放,敌人可以对玩家进行攻击
    代码思想利用精灵类绘制主角,自定义People类进行数据逻辑上的更新,Enemy类负责数据变更和绘图。利用列表模仿队列的方式,对玩家的攻击操作惊醒先后判断。UPDATE函数可以让玩家一次最多攻击一定数量的敌人,维持游戏平衡性。玩家类和敌人类中存在temp、clock变量和timetick函数,用来更新绘图上的数据。每个类都有JudgeList,避免敌人和玩家多次造成伤害,敌人的速度方向始终朝着玩家,利用三角函数实现。敌人用一个列表存储。
    1 评论 22 下载 2018-10-31 12:43:57 下载需要6点积分
  • 基于VC++的WIN32 API界面编程实现的飞机太空大战小游戏

    一、程序功能介绍炫酷精美的飞机太空大战,为体现“设计”的原创性,团队不使用游戏引擎,也没有抄袭任何源代码。全局使用团队自行编写的游戏类对象编写程序。建立以WIN32分辨率960*840的窗口为基础的GDI图形化画面,采用三级缓冲技术解决卡顿、闪屏问题。使用透明遮罩法透明化素材背景。
    游戏微信打飞机游戏中的飞机为图片源素材,后期使用PS技术上色。玩家可以利用键盘鼠标任意操作方式畅玩游戏,以击落更多敌机得到更高的分数为目标。加入敌人概率追踪算法、设置分数关卡、使用更新器调整敌人的更新速度。加入敌机和英雄的爆炸特效、增强游戏的可玩性与难度。
    玩家可自由切换多种不同杀伤力的子弹和巨大杀伤力的导弹,应付3种不同难度的敌人。利用随机生成的奖励补给,补充子弹、导弹、生命值。游戏已经可以实现射击子弹、实现击中、获取道具的单一音效(但无混响)。同时在图形界面中统计出每个种类子弹的数目,英雄自身的生命值、得分,拥有简易的排行榜。
    二、具体实现5.1 开发平台
    Microsoft Visual Studio 2013
    5.2 程序结构或流程图数据结构图

    游戏类图

    类关系图

    游戏流程图

    三、运行结果中型敌机爆照效果图

    小型敌机爆照效果图

    发射导弹效果图

    大型敌机爆照效果图

    被敌机碾爆的效果图

    简易排行榜的效果图

    三、调试
    敌机出现的时候出现残影:原因是以系统时间为随机数播种函数调用多次。生成的飞机形态一致,且坐标相同
    预设队列中的100辆敌机同时出现,原因是绘图函数调用频率很高。派送敌机函数的似乎迭代循环“一次”便派出100辆敌机。解决方法是设计一个定时器,根据调用次数来设计处在敌机队列中的派出状态
    吃到道具子弹补给超标,原因是飞机获得补给品后,补给品对应派出标志位没有设定为true
    敌机爆炸后依旧存在当前窗口(虽然绘图函数不再显示),消耗着英雄的子弹。原因是敌机的生命完结标志位没有设置为true,派送函数没有加上相应判断
    英雄当前子弹数为零的时候依旧可以实行射击,原因是射击函数的入口没有判断子弹数为零,持续绘图
    即使当前的子弹数为零,画面也没有子弹效果,点击射击键后数下,获得对应的子弹补给后瞬间发出成群的子弹束,原因是没有设置子弹存在位为false。(BUG出现之后该标志位尚未作为子弹对象的基本属性)
    玩家按下射击键超过50下(子弹层默认预置50个子弹对象),子弹射击不出,原因是子弹层预设的50颗子弹射击状态位设置完毕。解决方法是设计一个重置状态位的函数
    游戏进行一段是时间,生命值和分数值的显示会变大,原因是的对HFONT的字体句柄没有Delete,字体存储的数据有重叠;解决方法DeleteObject字体句柄
    飞机的爆炸效果一闪而逝,原因是没有设置延迟器并且派出函数遇到派出标志位为true但生命完结标志位亦为true的敌机,迭代器不再自增,使延迟器失效
    英雄画像出现压缩,原因加载位图的函数形参没有填写正确的分辨率
    英雄画像不断抖动,原因是贴图坐标与鼠标坐标的判等分支标写错误
    启动游戏出现1-3秒的卡顿,原因是产生3种类型敌机的迭代循环必须生成100辆敌机才跳出,而随机数对素数取模的不确定性很难短时产生所需敌机群。解决方法是使用STL模板库的置乱算法
    Plane类中的飞机移动绘图函数不声明为纯虚函数,子类中重写的虚函数会失效,并且编译器会提示函数重复声明的错误
    编译出现LNK2005错误,原因以头文件声明类与源文件说明实现方法的开发模式的限制,一旦在头文件中声明并定义函数实行,在CPP文件中再次写出实现方法会导致错误
    头文件的二次编译错误,解决方法在每个头文件首行加入#pragram once
    1 评论 15 下载 2018-10-31 12:35:55 下载需要4点积分
  • 基于C++实现的物品库存管理系统

    一、课题目标1.1 程序功能简介用文件系统实现的物品库存管理程序。一种商品的品种里面包括三个品牌,以品种为单位,可以实现商品品种的添加、删除、查找、显示等功能。
    1.2 课程设计要求
    组成商品品种的品牌不限数量,设置一个最大值(比如10),增加一个成员变量只是品牌的具体数量。商品品种的数量是每个品牌的数量之和
    设计输出每日进货与出货的统计表,要求进货与出货分别为两个文本文件,清单上列有进出货物的时间、品种描述、,具体品牌的进出货数量,如果是出货清单,在每笔出货的清单后还要有该次提取货物的总价格
    商品品种以品种代码的顺序存放

    二、具体实现2.1 程序总体方案
    封装Warehouse、Item、Brand三个类,Brand作为Item的复合对象,Item作为Warehouse的复合对象;两两之间启用双向关联特性
    设立增添、打印、搜索、删除物品、新增物品、新增品牌、删除品牌七个主功能;打印、搜索、删除下设分支功能拓展
    允许输入任意长度的物品与品牌
    全局使用vector.、list、map的STL容器实现代码,迭代使用迭代器操作。动态维护一个Warehouse实例化对象
    优化增强文件读写功能
    设置菜单类,增强人机交互能力
    程序给出必要的输入输出提示,警告非法输入,注重内存空间的申请与释放处理
    采用类声明在头文件,类实现在CPP的程序开发模式

    2.1.1 开发平台
    Microsoft VisualStudio 2013
    2.1.2 程序结构或流程图主程序流程图

    物品进库流程图

    打印信息流程图

    搜索信息流程图

    物品出库流程图

    2.2 所做改进2.2.1 改进一总述:新增仓库管理类(Warehouse.h)进行物品管理;
    2.2.1.1改进方案
    仓库类的基本属性应该包括记录Item类指针的动态Vector、以及物品的总重量的Totalweight、总进货价Totalprice、总物品数量TotalQuantity
    编写辅助排序、查找、删除等操作的辅助指针Map模板类型,优化时间和空间的复杂度
    提供按物品(品牌)编号、物品(品牌)名进出库的一系列的API接口,静态的查询
    仓库类为后续的出库进库实际应用中的最佳运输、调配物品的方案设计提供结构支撑(如使用BFS算法设计最佳运输路线)

    2.2.1.2 具体实现//此处只贴出核心的两个算法的代码;void Warehouse::inputItem(bool &flag){ system("cls"); int choice; Menu_Input(); cout << "Choice: "; cin >> choice; system("cls"); switch (choice) { case 1: long int quantity; cout << "*Enter Item Quantity: "; cin >> quantity; cout << endl; for (int i = 0; i < quantity;) { Item *item = new Item(); cout << "Enter item " << i + 1 << endl << endl; while (1) { item->inputData(); if (isExistCode(item->getItemCode())) { cout << endl; cout << "This item has existed.Input Item " << i + 1 << " again" << endl; } else break; } ++i; cout << endl; addItem(item); item->setAttributo(this); } this->Accumulate(); system("cls"); this->printfItemData_Code_AS(); this->writeData(flag); cout << "\nInput Competely.Return Main Menu?(YES/NO).\n\n"; break; case 2: this->loadData(flag); system("cls"); this->printfItemData_Code_AS(); cout << "\nInput Competely.Return Main Menu?(YES/NO).\n\n"; default: break; } choice_judge(flag);}Item* Warehouse::search_by_ItemCode(const string &search)//Search Item By Code.{ _ItemKeyValue::const_iterator tmp = this->SearchCodetoName.find(search); if (tmp != SearchCodetoName.end()) return tmp->second; else return NULL;}
    2.2.1 改进二总述:将Item.h、Brand.h封装成类,优化并新增Item与Brand类的基本属性配置;
    2.2.1.1 改进方案
    Item类新增source(货源地)、weight(总种量)、time(进货时间)三项基本属性
    将color属性移归Brand类,删除品牌数这个属性(可由Fuction获取_Brand的size)
    Item、Brand的名字编号采用更安全的string类储存,系统不限定物品或品牌的名字编号长度
    删除链表类;直接采用STL模板的list容器增强系统的可维护性
    支持任意数量的物品(品牌)进库
    对于Item、Brand每一项基本属性都提供相应成套的set、get方法
    加入const修饰符,对不同功能的函数限定调用权限,对不同的数据限定读写权限

    2.2.1.2 具体实现void setDate(const string &Name, const string &Code, const _Brand &brand);void setItem_Name(const string &Name){ this->Item_name = Name;}void setItem_Code(const string &Code){ this->Item_code=Code; }void setItem_source(const string&source){ this->Source = source; }void setItem_time(const char&ch){ this->Time += ch; }const float &getPurchasePrice()const{ return this->PurchasePrice; }const float &getWeight()const{ return this->Weight; }const long int &getQuantantity()const{ return this->AllQuantity;}const string & getSource()const{ return this->Source; }const string &getTime()const{ return this->Time; }void Item::inputData(){ long int quantity; cout << "Enter Item Name: "; cin >> Item_name; cout << endl; cout << "Enter Item Code: "; cin >> Item_code; cout << endl; cout << "**Enter Brand All Quantity: "; cin >> quantity; cout << endl << endl; for (int i = 0; i < quantity;) { cout << "Enter Brand: " << i + 1 << endl; Brand *brand = new Brand(); brand->inputData(); if (isExistSameBrand(*brand)) { cout << "Brand has existed.Input Brand " << i + 1 << " again." << endl << endl; continue; } cout << endl << endl; ++i; brand->setAttribution(this); BrandName.insert(BrandName.end(), brand); } cout << "Enter Item Source: "; cin >> Source; time_t timer = time(NULL); char str[30]; ctime_s(str, 30, &timer); Time = str; this->Accumulate();}
    2.2.3 改进三总述:优化并加强文件读写操作;
    2.2.3.1 改进方案
    由于新增动态输入删除操作,文件对仓库信息的写入以及读取不能局束限定数量或内容
    Write操作要求记录着物品的总数量。方便录入Vector<Item *>的数据类型;物品的相应信息按编码字典序且Item属性都顺序记录在文件当中,满足双向读写的要求
    Load操作要求首先录入物品的总数量,提前让系统知道录入的物品数量,动态开辟足够的存储空间,加载物品的信息
    用户自定义输入的物品数据,作为新增的Item及时地记录进文件中
    系统可选用默认的(Default)文件名、用户自定义的文件名加载仓库信息
    用户录入物品信息完毕后,可以自行命名文件名(Cunstom),方便下一次打开查询
    保存的物品信息选用二进制加密,用启动的系统进行解密,防止人为的意外和篡改

    2.2.3.2 具体实现void Warehouse::writeItem(Item &item){ size_t len_Name = item.getItemName().length(), len_Code = item.getItemCode().length(), len_Source = item.getSource().length(), len_Time = item.getTime().length(); long int size = item.getBrandSize(); filestream.write((char*)&len_Name, sizeof(size_t)); filestream.write(item.getItemName().data(), len_Name); filestream.write((char*)&len_Code, sizeof(size_t)); filestream.write(item.getItemCode().data(), len_Code); filestream.write((char*)&len_Source, sizeof(size_t)); filestream.write(item.getSource().data(), len_Source); filestream.write((char*)&len_Time, sizeof(size_t)); filestream.write(item.getTime().data(), len_Time); filestream.write((char *)&size, sizeof(long int)); item.writeBrand();} void Warehouse::loadItem(){ Item * item = new Item(); long int size; char ch_tmp; string source; size_t len_Name, len_Code, len_Source, len_Time; filestream.read((char*)&len_Name, sizeof(size_t)); for (size_t i = 0; i < len_Name; ++i) { filestream.read((char*)&ch_tmp, 1); item->setItem_Name(ch_tmp); } filestream.read((char*)&len_Code, sizeof(size_t)); for (size_t i = 0; i < len_Code; ++i) { filestream.read((char*)&ch_tmp,1); item->setItem_Code(ch_tmp); } filestream.read((char*)&len_Source, sizeof(size_t)); for (size_t i = 0; i < len_Source; ++i) { filestream.read((char*)&ch_tmp, 1); item->setItem_source(ch_tmp); } filestream.read((char*)&len_Time, sizeof(size_t)); for (size_t i = 0; i < len_Time; ++i) { filestream.read((char*)&ch_tmp, 1); item->setItem_time(ch_tmp); } filestream.read((char*)&size, sizeof(long int)); for (long int i = 0; i < size; ++i) item->loadBrand(); item->setAttributo(this);}void Item::loadBrand();//同上void Item::writeBrand();//同上
    2.2.4 改进四总述:优化物品进库时的静态查询(search) 、插入,以及物品出库时动态的物品删除(remove)等相关操作。
    2.2.4.1 改进方案
    层次关系为Warehosue维护Item,而Item维护Brand
    对于Warehouse类,存储物品的Vector可以无序,但提供两组一对一高效的map<string, Item \*>数据结构,按物品名字、编号的AVL树搜索算法把搜索、插入操作的都降至logn
    对于Item类,考虑到实际应用中品牌的多样化,使用容器List<Brand *>存储物品的品牌,满足频繁的删除品牌类的插入操作。同时提供两组一对一高效的map<string, Brand *>数据结构,时间上的优化同上

    2.2.4.2 具体实现void Warehouse::addItem(Item *item){ TotalQuantity += item->getQuantantity(); Totalprice += item->getPurchasePrice(); Totalweight += item->getWeight(); SearchNametoCode.insert(Pair(item->getItemName(), item)); SearchCodetoName.insert(Pair(item->getItemCode(), item));//logn插入; Totalitem.push_back(item);}void Item::addBrand(Brand *brand){ BrandName.push_back(brand); PurchasePrice += brand->GetBrand_quantity()*brand->GetBrand_price(); Weight += brand->GetWeight()* brand->GetBrand_quantity(); AllQuantity += brand->GetBrand_quantity();}Item* Warehouse::search_by_ItemCode(const string &search)//Search Item By Code.{ _ItemKeyValue::const_iterator tmp = this->SearchCodetoName.find(search); if (tmp != SearchCodetoName.end()) return tmp->second; else return NULL;}void Warehouse::remove_by_code(const string &code)//Delete Item By Code.{ Item * tmp = search_by_ItemCode(code); if (tmp != NULL) { release(tmp); system("cls"); printfItemData_Code_AS(); cout << "\n\nThis Item has been removed."; } else cout << "\n\nThe Item don't exist in this warehouse.";}void Warehouse::remove_by_range(string & _First,string &_Last){ if (_First > _Last) swap(_First, _Last); sort(Totalitem.begin(), Totalitem.end(),Item::CMP_By_Code); Item *tmp = search_by_ItemCode(_First); _Item::iterator it; if (tmp != NULL) { for (it = Totalitem.begin(); it != Totalitem.end(); ++it) if ((*it) == tmp) break; for (; (it != Totalitem.end()) && ((*it)->getItemCode() != _Last);) { SearchCodetoName.erase((*it)->getItemCode()); SearchNametoCode.erase((*it)->getItemName()); it = Totalitem.erase(it); } system("cls"); printfItemData_Code_AS(); cout << "\n\nRemove Competely."; } else cout << "Remove Failed.Range is UNKOWN.\n\n";}
    2.2.5 改进五总述:新增友好的控制台人机交互画面与菜单;
    2.2.5.1 改进方案
    菜单的打印图案存储在TXT中,由Menu类设立统一的全局函数实行管理
    对于主菜单,提供8个功能接口
    对于Add、Printf、Search、Remove的API,提供清晰的分级菜单,方便用户选用其中的分级功能
    打印的数据列出属性标题,采用明显的分隔线,便于阅览
    非法的输入给出错误的提示;每一个系统功能执行完毕,提供Return Main Menu的选项
    如果当前仓库不存在任何物品信息,用户又启动了系统的Printf、Search、Remove等功能,自动弹出Add Item Information的分级菜单,提示用户是否录入数据
    控制台采用白底黑字,增强视觉效果

    2.2.5.2 具体实现void Menu_Main();void Menu_Input();void Menu_Search();void Menu_Search_By_Item();void Menu_Search_By_Brand();void Menu_Show();void Menu_Remove_Item();void Menu_Updateitem();void Menu_Line1();void Menu_Line2();void Menu_NoItem();void Menu_Loadfromfile();void Menu_Writefile();void Menu_Writefile();{ ifstream infile("Menu\\Menu_Main.txt"); if (!infile.is_open()){ cout << "Error opening file"; exit(1); } while (!infile.eof()) { char buffer[500]; infile.getline(buffer, 500); cout << buffer << endl; }}while (1) { cin >> choice; if (choice.length()==1 && (choice[0]<='9')) { switch (choice[0]) { case '1': warehouse_First->inputItem(flag); //增(进库); break; case '2': warehouse_First->printfWarehouse(flag);//打印; break; case '3': warehouse_First->search(flag);//搜索; break; case '4': warehouse_First->updateItem(flag);//更新; break; case '5': warehouse_First->remove_Item(flag);//删除(出库); break; case '6': warehouse_First->addBrand(flag);//新增物品品牌; case '7': warehouse_First->removeBrand(flag);//删除物品品牌; break; case '8': delete warehouse_First; exit(0); default: break; } break; }
    2.2.6 改进六总述:提供多样化的打印(排序)操作;
    2.2.6.1 改进方案
    提供按名字字典序升降序,按编号字典序升降序四种不同的打印方式;排序生成方式为对象的索引,而非对象本身
    借助Warehouse.h中的map<string, Item *>,以及Item.h中的map<string, Brand *>,可以使用正向迭代器,逆向迭代器遍历map,按字典序打印
    为节省一定的内存空间和时间,用户未曾启动打印功能时,不实行对map的插入操作(AVL树构建)
    用户启动打印(排序)时,检验map是否为空:不为空,有序清单已经生成;若为空,启动排序算法,构建树状的指针有序物品清单
    2.2.6.2 具体实现
    _BrandKeytoValue& Item::Sort_by_Code(){ for (_Brand::const_iterator it = BrandName.begin(); it != BrandName.end(); it++) { Brand_Code_ASOrder.insert(_BrandPair((*it)->GetBrand_code(), *it)); } return this->Brand_Code_ASOrder;}bool Item::isorted_Brand_Name(){ if (!Brand_Name_ASOrder.empty()) return true; else return false;}void Warehouse::printfItemData_Code_AS(){ for (_ItemKeyValue::const_iterator it = SearchCodetoName.begin(); it != SearchCodetoName.end(); ++it) { Menu_Line1(); printfItemTitle(); it->second->printfData(); it->second->printfBrandData_Code_AS(); Menu_Line2(); cout << endl << endl; }}//按品牌编号字典序升序打印void Warehouse::printfItemData_Code_DES(){ for(_ItemKeyValue::const_reverse_iterator it = SearchCodetoName.rbegin(); it != SearchCodetoName.rend(); ++it) { Menu_Line1(); printfItemTitle(); it->second->printfData(); it->second->printfBrandData_Code_DES(); Menu_Line2(); cout << endl << endl << endl; }}//按品牌编号字典序降序打印
    2.2.7 改进七总述:应用双向关联特性;消除内存泄漏问题。
    2.2.7.1 改进方案
    Brand类设置私有的Item *指向所属的物品,Item类设置私有的Warehouse *指向所属的仓库
    当动态的删除Item名下的Brand,底层函数能够通过关联的Item指针反向更新该Item的重量、进货价格等属性;同理Item相对Warehouse亦是如此
    对于Brand、Item的单一删除和批量删除,注意要释放其内存空间
    对于Warehouse的析构函数,调用releas方法释放申请Item的空间,Item的析构函数,调用release方法释放申请Brand的空间;每个Warehouse、Item、Brand当且仅当delete一次

    2.2.7.2 具体实现void Brand::setAttribution(Item *newAttribution){ if (Attributedto == newAttribution) return; Attributedto = newAttribution; if (newAttribution != NULL && (!newAttribution->isExistSameBrand(*this))) Attributedto->addBrand(this);}void Item::setAttributo(Warehouse *warehouse){ if (warehouse == Attributeto) return; Attributeto = warehouse; if ((warehouse != NULL) && (!warehouse->isExistCode(Item_code))) warehouse->addItem(this);}void Warehouse::release(Item *item){ SearchNametoCode.erase(SearchNametoCode.find(item->getItemName())); SearchCodetoName.erase(SearchCodetoName.find(item->getItemCode())); delete item;}void Item::release(Brand *brand){ if (!Brand_Name_ASOrder.empty()) Brand_Name_ASOrder.erase(Brand_Name_ASOrder.find(brand->GetBrand_Name())); if (!Brand_Code_ASOrder.empty()) Brand_Code_ASOrder.erase(Brand_Code_ASOrder.find(brand->GetBrand_code()));//2logn删除; BrandName.erase(lower_bound(BrandName.begin(), BrandName.end(), brand));//logn删除 delete brand;}
    三、调试
    类函数在头文件中的声明不能加上{};这意味声明并定义了一个函数,在cpp的函数实现会失效
    对于Item、Brand的排序需要编写cmp函数,不然algorithm库中sort排序会出错
    诸如map<const string, Item *>插入时已经有序
    对同一Item*回收两次,造成对野指针的重复delete出错
    对Brand的空间回收内存访问出错,原因Brand的析构函数已经自动对Brand*申请的空间释放,在Item层再次启动delete
    Brand搜索算法中容器返回的迭代器必须判断不为end()才可以对搜索到的Band指针进行返回;否则内存会访问出错
    文件输入的方式的loadItem/loadBrand忘记加入setAttributo函数设置双向关联,结果造成对象指针的未初始化
    文件对Warehouse的写入顺序不严格,反向写入时造成写入乱码
    批量删除越界程序崩溃,优化为若越界默认的删除最大范围内的物品
    头文件相互包含重复编译,解决方法在开头加入#program once
    对于重载的搜索函数书写不当,Linked发生错误

    程序主界面:
    1 评论 15 下载 2018-10-31 12:25:04 下载需要6点积分
  • 基于JAVA实现的超级马里奥(Super Mario)游戏

    一、项目简介刚进入的时候会有一个界面,为地图编辑器。可以使用此编辑器进行地图编辑,地图编辑器的内容包括:关卡、向左箭头、带有金币的砖块、带有花朵的砖块带有蘑菇的砖块、带有星星的砖块、普通砖块、向左运动的板栗仔、向右运动的板栗仔、向左运动的乌龟、向右运动的乌龟、金币、带有食人花的管道、普通管道、洞、向右的箭头、橡皮擦、可以使用鼠标点击图标然后拖动到面板上点击面板进行地图编辑,橡皮擦可以擦除已经建立好的模型。
    部署完地图之后可以选择下一关进行下一个关卡的编辑,也可以点击开始游戏开始游戏。游戏开始后从编辑的第一关卡开始进行闯关,人物可以移动通过ad键进行控制,可以跳跃,通过k控制,跳的时候可以跳到管子和砖块上面。
    人物有两种状态:大马里奥和小马里奥。小玛丽奥可以撞普通的砖块或者带有包含物的砖块使得砖块可以向上稍微移动,砖块上的一些包含物也会随着砖块移动。大马里奥可以顶破普通砖块。
    怪物分为三种,分别为:板栗仔、乌龟和食人花。马里奥可以通过跳跃的方式踩死怪物,板栗仔在被踩的时候会变扁,乌龟被踩的时候。走动状态会变成龟壳状态,龟壳状态被碰到可以变成跑动的龟壳状态,跑动的龟壳可以杀死马里奥。板栗仔和其他的乌龟。运动的龟壳在运动的时候被马里奥踩到会变成静止的龟壳,食人花长在管道中,会定时出现对管道上方的物体进行攻击,当马里奥踩在管道上的时候不会出现。
    还有三种物体是包含在砖块中的,分别是星星、蘑菇、花朵。马里奥自下向上顶砖块之后砖块上方会生长出相应的植物星星和蘑菇会向右方向行走,花朵会在原地。马里奥可以通过触碰的方式吃掉植物,不同植物有不同的加成效果。其中,吃掉蘑菇之后会变成大马里奥。吃掉星星之后会变成无敌状态。吃掉花朵之后会有发射子弹的技能。
    任何物品,尤其是可移动物,包括子弹,在碰到洞之后会掉落到洞中人物掉落之后会损失一命,人物一共有五条生命。每次正面碰到乌龟或者板栗仔,或者掉落到洞中之后便会损失一条生命,每次损失生命则该关卡从头开始当五条生命全部损失之后便会到game over状态。
    当马里奥走到地图的最后一个模型之后的位置的时候说明本关通过,本关通过时会有马里奥跳下拉动旗帜旗帜拉倒底端的时候会向右跑到城堡位置。跑到城堡位置即属于本关卡已经通过,则消除所有的加成状态转到下一关卡。
    最后通关所有的关卡即为game ends,跳跃的时候有重力效应,降落的会越来越快游戏界面上方会有剩余生命,当前时间为0的时候会损失一命,还有计分系统。当玩家杀死怪物,或者吃掉某种可生长物,或者过关的时候都会获取相应的分数加成。分数显示在面板上方。吃掉金币会有金币数量统计,统计结果在生命右边。本项目的亮点在于应用ioc技术的地图编辑器和精美的人物模型。
    二、需求分析人物跳跃的重力,条约落下的时候碰到其他的硬物可以停止下落。踩死怪物。怪物死亡方式不同展现的画面不同。吃东西,有属性加成。大马里奥顶破砖块。小玛丽奥顶砖块砖块可跟随移动。砖块上方的东西可以跟随移动。地图编辑功能。声音功能。人物胜利拉旗进城堡。
    三、系统设计
    本项目共有20个公共类,5个接口。分成三种类,分别是:控制类、模型类、工具类
    功能方面有两大块,分别是地图编辑器和正式游戏
    地图编辑器部分使用了spring框架的ioc技术

    程序功能流程图如下图所示:

    控制类中有两个类分别为Main和Control作用分别为控制地图编辑,地图编辑的思路如下:玩家点击图标之后鼠标的状态变成点击的图标的状态值,本类中有一个map键值分别为Integer和model鼠标移动到某位置点击之后会使integer加一。构造出相应的model然后put到map中如果用户点击的是橡皮擦。那么会计算哪一个类在橡皮擦点击的位置,并且把相应的位置的model设为忽略。最后解析这个map构造xml文件和保存着各个类的数量的一个properties文件Control类通过解析xml和properties文件解析关卡信息还原用户编辑的地图。
    还有一个全局的properties为game.properties保存着关卡数目。在点击开始游戏之后开始运行Control类中的work方法。Work方法的作用是初始化整个游戏的完整页面读入xml中的内容实例化对象存到容器中。然后启动paintThread线程画出面板,启动其他的必要线程进行工作,根据用户的操作对容器中的对象的一些参数进行改变呈现不同的视觉效果。
    四、系统实现本项目由于需要实现用户自由设计地图,所以应该尽量降低耦合度,从全局的角度出发,对类的设计应该分为实物模型,统筹控制,工具,抽象出来的接口。接下来一一介绍。类结构图如下图所示:

    统筹控制包含Main和Control在上一部分已经介绍。接口被抽象出来以下几种:

    Dangerous类所有的可以杀死主角的实物模型类应该去实现该接口
    Flint类,砖块和管子应该去实现该接口。Growable接口,所有的可以被马里奥从砖块中顶出的实物模型应该实现该接口。Kill接口,所有的可以伤害到别的实物模型类的类都应该实现该接口。Moveable接口,所有的可以移动的物体都应该实现该接口

    实物模型类一共有12个:

    Badflower类是食人花类,实现Dangerous接口
    Bullet类是子弹类,实现Moveable和Kill接口
    Flower类是吃了以后可以发射子弹的类,实现了Growable接口
    Hole类是地面上存在的洞类。没有去实现任何接口
    Mario类,是主角类,实现了Moveable,Kill接口
    Money类,因为可以直接被主角吃掉并且在砖块上被顶出之后不需要生长移动过程所以不实现任何接口
    Monster类是板栗仔,实现了Dangerous,Moveable接口
    Mushroom类是吃了以后变大的蘑菇类实现了Growable和Moveable接口
    Pipe是管道类实现了Flint接口
    Star是吃了以后变成无敌状态的星星,实现了Growable和Moveable接口
    Turtle是乌龟类,实现了Dangerous,Kill,Moveable接口
    Wall是砖块类,实现了Flint方法

    还有6个工具类,其中的方法和字段大部分是静态的和final的:

    ApplicationUtil类可以通过传入的关卡值去加载spring上下文,为程序提供对象实例
    CrashType定义了一些物体之间的碰撞类型的常量
    ImageTool类中包含了程序用到的所有的图片以及为了克服延迟加载而写的事先加载所有图片的方法
    Null类是一个Growable类的空实现,因为在构造砖块的时候定义构造方法里面应当传入所包含的可生长物,而在使用spring框架进行实例化得时候不允许出现null.本人又极不愿意在写另外一种构造方法,所以索性构造一个Growable接口的空实现类,通过传出特殊的Type值进行识别
    Property类,用于解析配置文件,获取数据
    SoundTool类包含所有的使用到的音乐,以及静态的播放音乐的方法

    五、调试改错在实现的过程中出现了很多错误。比如声音播放问题和人物碰撞检测的问题等。不过最后解决的还算满意。
    六、美工素材本项目是一个人写的,代码和图片美工都是自己实现的。由于互联网上找不到相关素材,所以本人现学的ps,通过录制游戏中的人物动作分帧截图,使用抠图等技术自己做的图。
    七、总结要有统筹整个项目的意识。对整个项目有总体的把握。类的编写应当事先分好类,分清楚每种类的任务。最大化解耦,以免改动的时候涉及到过多的地方。分清楚每个类的任务。合理设计避免冗余。
    八、关键代码碰撞检测部分代码:
    public int getCrashType(int down,int direction,Rectangle rec1,Rectangle rec2)//rec1为wall,rec2为Mario 获取撞击类型 { //rec1是硬物。rec2是移动物 if(die)return CrashType.NO_CRASH; int rec1X=(rec1.x+rec1.x+rec1.width)>>1; int rec1Y=(rec1.y+rec1.y+rec1.height)>>1; int rec2X=(rec2.x+rec2.x+rec2.width)>>1; int rec2Y=(rec2.y+rec2.y+rec2.height)>>1; int width=rec1.width+rec2.width; int hight=rec1.height+rec2.height; if(rec2Y>=rec1Y) { if(rec1X>=rec2X) { if(down!=1||(rec1X-rec2X)/((double)width)>((rec2Y-rec1Y)/(double)hight)+CrashType.POINT) return CrashType.WALL_L;else { if(control.getMario().isCanWork())work(); control.getMario().down(); return CrashType.WALL_D; } }else { if(down!=1||(rec2X-rec1X)/((double)width)>((rec2Y-rec1Y)/(double)hight)+CrashType.POINT) return CrashType.WALL_R;else { if(control.getMario().isCanWork())work(); control.getMario().down(); return CrashType.WALL_D; } } }else { if(rec1X>=rec2X) { if((rec1X-rec2X)/((double)width)>=((rec1Y-rec2Y)/(double)hight)+CrashType.POINT) return CrashType.WALL_L;else { return CrashType.WALL_U; } }else { if((rec2X-rec1X)/((double)width)>=((rec1Y-rec2Y)/(double)hight)+CrashType.POINT)return CrashType.WALL_R;else return CrashType.WALL_U; } } }
    马里奥跳跃代码:
    private class JumpThread extends Thread//跳的线程 { private int n=jumpHight; public void run() { if(down==-1)return; if(downThread!=null) { downThread.stop(); downThread=null; } if(jumpThread!=null) { jumpThread.stop(); jumpThread=null; } jumpThread=this; //while(!Control.isALL_START()){try{sleep(Control.TIME);} catch (InterruptedException e){}} SoundTool.play(SoundTool.jumpSound); try { int site=locaY; double count=Math.sqrt((2*0.085*n)); for(int i=site;count>0;i-=(count-=0.1))//向上跳的时候改变状态 { if(getCrashType()&&(crashType==CrashType.WALL_D||crashType==CrashType.WUWL||crashType==CrashType.WUWR))//如果发现从下撞击了硬物则跳出向上的过程改为向下 { down=-1; break; } locaY=i; sleep(10); down=1; } new Down().start(); }catch(Exception e) { e.printStackTrace(); } down=0; } }
    马里奥降落代码:
    private class Down extends Thread//二类下落线程 //控制最终下落的线程如果在正常下落时由于碰撞被打断则启动该线程监视是否需要再次落下 { public void run() { if(down==1)return; //while(!Control.isALL_START()){try{sleep(Control.TIME);} catch (InterruptedException e){}} if(downThread!=null) { downThread.stop(); } downThread=this; // System.out.println("enter down!"); // while(down!=-1&&locaY!=control.getCutLine())//没有落地面上一直在循环 while(locaY<control.getCutLine()) { boolean flag=false;// int site=locaY; // System.out.println(crashType+" "+down+" "+canWork); if((getCrashType()&&(crashType==CrashType.NO_CRASH)&&(down!=1))||canWork==false)//多线程重要判断应该靠紧 { flag=true;//已经下落 double count=1; for(int i=site;i<=control.getCutLine();i+=(count+=0.1)) { down=-1; locaY=i; downSpeed=count; // System.out.println(i+" "+control.getCutLine()); if(getCrashType()&&(crashType==CrashType.WALL_U||crashType==CrashType.PIPE_U||crashType==CrashType.WUWL||crashType==CrashType.WUWR))//如果在某个硬物的上方、启动二类下落线程且退出本线程 { down=0;//落道硬物上面则运动状态为0 downSpeed=0; downThread=null; new Down().start(); setCanWork(true); return; } try { sleep(10); } catch (InterruptedException e) { down=0; downThread=null; downSpeed=1; setCanWork(true); new Down().start(); } } locaY=control.getCutLine(); } if(flag)//在该线程中如果已经下落则跳出 { if(!control.getMario().isDownDie()) for(int j=0;j<control.getHoles().size();j++) { if(control.getHoles().get(j).canPaint()) control.getHoles().get(j).DownDie(control.getMario()); } if(!control.getMario().isDownDie())downSpeed=1; down=0; downThread=null; setCanWork(true); return; } } }
    1 评论 92 下载 2018-10-31 12:18:12 下载需要4点积分
  • 基于Android实现的页面置换模拟

    一、使用说明1.1 项目简介请求分页分配方式的模拟,包括页面、页表、地址的转换和页面置换过程,并通过该模拟过程加深对请求调页系统的原理和实现过程的理解。并且学习Android环境下使用Java编写LRU、FIFO算法。
    这次项目我偏向于算法的实现,目的是为了更好的比较出FIFO算法和LRU算法两种算法的不同之处,所以我实现了两种页面置换方式和两种指令执行顺序,工作量比项目要求大了一些,所以UI方面没有时间和精力去做更细致的绘画,希望助教和老师能原谅。
    1.2 项目要求
    假设每个页面可存放10条指令,分配给一个作业的内存块为4。模拟一个作业的执行过程,该作业有320条指令,即它的地址空间为32页,目前所有页还没有调入内存
    在模拟过程中,如果所访问指令在内存中,则显示其物理地址,并转到下一条指令;如果没有在内存中,则发生缺页,此时需要记录缺页次数,并将其调入内存。如果4个内存块中已装入作业,则需进行页面置换
    所有320条指令执行完成后,计算并显示作业执行过程中发生的缺页率
    本次项目采取了两种页面置换算法,FIFO算法和LRU算法
    本次项目采取了两种指令执行顺序,随机顺序和项目文档要求中的1:2:1随机顺序,即50%的指令是顺序执行的,25%是均匀分布在前地址部分,25%是均匀分布在后地址部分
    视图一显示了每一步请求调页的指令信息,视图二显示了4块内存的页面使用情况

    1.3 操作手册运行程序后,首先进入操作界面,如图。

    第一步,选择页面置换算法和指令执行顺序。

    第二步,点击逐步演示按钮,开始逐步模拟请求调页存储管理方式。上方的缺页次数和缺页率每一步都会发生相应的改变,视图一显示了每一步请求调页的指令信息,视图二显示了4块内存的页面使用情况。可以一直显示到最后一条指令,共320条指令。

    第三步,点击复位按钮,可以使所有数据恢复到初始状态。

    第四步,重新选择页面置换算法和指令执行顺序,点击自动演示按钮,程序会自动演示完320条指令的执行过程。此时视图一可以查看每条指令的信息,由于是自动演示,过程太快,视图二不显示内存页面的使用。

    1.4 注意事项:
    逐步演示过程中不可更改指令执行顺序或页面置换算法,否则数据会出错
    每次演示必须提前选择好指令执行顺序和页面置换算法,否则无法执行程序

    二、概述2.1 基本思路LRU算法:每次访问一条指令的时候,先判断该指令所在页是否存在内存当中,如果存在,则未发生缺页,若不存在,则证明发生缺页。将缺页次数加一后,继续分两种情况判断,第一种情况为4个内存块中还有内存块未装入作业,将此页装入内存块中;第二种情况为内存块已满,需要进行页面置换,此步也是LRU算法的核心:首先计算当前4个内存块的页面哪个页面最久未被调用过,然后将该页面从相应内存块中取出,置换进入需要的页面。
    FIFO算法:该算法前面步骤类似于LRU算法,也需要先判断是否发生缺页,是否有内存块未装入作业。不同之处在于置换的方式,FIFO算法采用先进先出的原则,最先存入内存块中的页面将被置换出去。
    2.2 主要文件
    MainActivity.class & activity_main.xml (主文件,即操作界面)
    Iistview_item.xml(视图一)

    三、具体实现3.1 页面置换方法:LRU算法LRU算法共分三大步:

    首先计算出当前访问指令所在页,然后将该页与4个内存块中的页相比较,观察是否存在缺页现象,如果不在内存内则将loss置为true,表明缺页,进入下一步。


    如果的确发生缺页,首先将缺页次数加一,之后判断是哪种缺页情况,若是4个内存块未满,则将该页调入未满的内存块中。


    若内存块已满,则需要进行页面的置换,此步是LRU算法的核心,即将最久未被调用的页置换出去。项目中采取的方法是,首先计算内存中每一个页面最近一次被调用的位置,之后将这四个位置进行比较,挑选最远的位置作为point,即作为被置换出去的页面。

    point作为该算法中的内存指针,该指针指向的是每次页面应被存入的内存位置,在LRU算法中,point指向最久未被调用的页所在内存块位置。
    3.2 页面置换方法:FIFO算法FIFO算法相较于LRU算法更为简单,更好实现,同样算法的基本思想为三步,前两步与LRU算法完全相同,差别在第三步置换方式上有所不同。FIFO算法采取先进先出原则,因此每次发生缺页时point的指向每次递增一次,在四个内存块中按一定的顺序循环增加。

    3.3 指令执行顺序:Random算法程序提供了两种指令执行顺序,一种是随机执行,从0~319条指令中随机抽取指令访问,并保证320条指令都能得到执行。

    3.4 指令执行顺序:1:2:1该指令执行顺序是按照项目要求中给出的指令执行方法。在0-319条指令之间,随机选取一个起始执行指令,如序号为m,顺序执行下一条指令,即序号为m+1的指令。通过随机数,跳转到前地址部分0-m-1中的某个指令处,其序号为m1。顺序执行下一条指令,即序号为m1+1的指令。通过随机数,跳转到后地址部分m1+2~319中的某条指令处,其序号为m2。顺序执行下一条指令,即m2+1处的指令。重复跳转到前地址部分、顺序执行、跳转到后地址部分、顺序执行的过程,直到执行完320条指令。

    3.5 逐步演示和自动演示逐步演示需要每次点击按钮来实现,每次点击时程序会进行判断,若当前访问的指令数小于320则可以继续访问,若达到320则程序执行完。每一步缺页数和缺页率都会发生变化,视图二也会体现相应的内存页面调换过程。
    自动演示只需点击一次自动演示按钮即可,程序自动演示完320条指令并直接显示结果。视图二不会体现相应内存页面调换过程,但会计算出最终缺页数和缺页率。
    3.6 复位点击复位按钮将所有数据恢复到初始状态,包括指令执行数、缺页数、缺页率以及内存等信息。
    四、总结这次的项目主要是学习操作系统知识中的页面调度算法,其中涉及的知识点包括页面的两种调度算法,FIFO算法和LRU算法。通过这次项目不仅是对操作系统中这一方面的知识点了解更深,对Android的学习有了更多的了解。
    这次项目我偏向于算法的实现,目的是为了更好的比较出FIFO算法和LRU算法两种算法的不同之处,所以我实现了两种页面置换方式和两种指令执行顺序,工作量比项目要求大了一些,所以UI方面没有时间和精力去做更细致的绘画,希望助教和老师能原谅。
    通过本次程序我发现,无论哪种指令执行顺序,LRU算法相比于FIFO算法更适用于请求调页存储管理,LRU算法的缺页次数更少一些,缺页率更低一些,而且1:2:1的指令执行顺序缺页率比Random随即将算法更低。
    1 评论 3 下载 2018-10-31 11:55:09 下载需要8点积分
  • 基于QT实现的图的遍历演示

    1 问题分析和任务定义1.1 问题描述很多涉及图上操作的算法都是以图的遍历操作为基础的。试写一个程序,演示在连通的无向图上访问全部结点的操作。
    1.2 基本要求以邻接多重表为存储结构,实现连通无向图的深度优先和广度优先遍历。以用户指定的结点为起点,分别输出每种遍历下的结点访问序列和相应生成树的边集。
    1.3 测试数据任选国内城市,起点为合肥,暂时忽略里程。
    1.4 问题分析及任务定义此程序需要完成以下操作:使用文件读取数据,以邻接多重表为存储结构,构成一个连通的图。用户输入一个起始点,从起始点开始对图分别进行深度优先和广度优先遍历。分别输出每种遍历下的结点访问序列和相应的生产树的边集。
    程序的执行流程如下图所示。

    1.5 实现本程序需要解决的问题
    如何利用从文件读取的数据构成无向图
    如何实现从用户指定的点开始遍历
    遍历完后如何输出遍历过得点的序列和边集
    如何利用图形化动态的演示遍历过程
    如何提高用户体验

    本程序的难点在于如何创建邻接多重表,和如何遍历所有城市。
    设图的结点20-30个,每个结点用一个编号表示(如果一个图有n个结点,则它们的编号分别为1,2,…,n)。通过输入图的全部边(存于数据文件中,从文件读写)输入一个图,每个边为一个数对,可以对边的输入顺序作出某种限制。注意,生成树的边是有向边,端点顺序不能颠倒。
    2 数据结构的选择和概要设计城市与城市之间是没有方向的,构成的无向图采用邻接多重表来实现,主要表示无向图的的各个边和结点,并通过对邻接多重表的操作实现对无向图的深度优先和广度优先遍历。创建AMLGraph类,其包含的属性和方法如下图所示。

    其中adjMulList是顶点数组指针,所用的顶点结点以顺序方式存储在一维数组中;vexnum和edgenum是无向图的结点个数和边个数;cityMap类是用来存储所有从文件中读取的城市名和坐标信息的类,points、dfs_str、bfs_str是用来存储遍历的城市的坐标和名称的变量。
    GreatAMLGraph函数用来构建邻接多重表,LocationVex函数通过城市名找到该城市在表结点的位置。AMLGraph、~AMLGraph函数分别是类的构造函数和析构函数,BFS_Traverse、DFS_Traverse分别是广度优先和深度优先遍历的函数。
    VexNode结构体是点结点(城市结点),name是城市的名称,firstEdge是EdgeNode类型的指针,指向该顶点的第一条边的结点。
    EdgeNode结构体是边结点,isVisited为标志域,用来标记该条边是否已被访问过;ivex为顶点域,指示依附于这条边的一个顶点的位置(序号);jvex指示另一个顶点的位置;ilink为链域,指向依附于第一条边的结点。
    3 详细设计和编码3.1 邻接多重表的建立
    从文件中读取所有城市名、坐标和线路信息。利用头插法建立邻接多重表。
    for (int i = 0; i < edgenum; ++i){ EdgeNode * e = new EdgeNode; e->isVisited = false; string v1, v2; inFile >> v1 >> v2; int a = LocationVex(v1); int b = LocationVex(v2); e->ivex = a; e->jvex = b; e->ilink = adjMulList[a].firstEdge; e->jlink = adjMulList[b].firstEdge; adjMulList[a].firstEdge = e; adjMulList[b].firstEdge = e;}
    3.2 深度优先遍历先调用DFS_Traverse函数,初始化visited[i]数组,再调用DFS(i),其中i为用户指定城市的索引。在DFS函数中,先访问表结点中第i个点,在判断该顶点的另一端是否被访问,如果没有访问,就在调用DFS(i->jvex),形成递归,直到访问到该顶点的另一端被访问过后,在访问与i相连的其它边,当与i相连的所有边都被访问完时,结束循环。
    while (e){ if (e->ivex == i) { if (!visited[e->jvex]) DFS(e->jvex); e = e->ilink; } else { if (!visited[e->ivex]) DFS(e->ivex); e = e->jlink; }}
    e为EdgeNode类型的指针变量。时间复杂度为O(n+edgeNum)。
    3.3 广度优先遍历先调用BFS_Traverse函数,初始化visited[i]数组,再调用BFS(i),其中i为用户指定城市的索引。BFS函数中,先访问表结点中第i个点,并将该点放入队列q中,在循环中不断取出队列的第一个元素,直到队列为空时,所以点遍历完成,结束该循环。取出队列第一个元素后,再判断该顶点的另一端是否被访问,如果没有访问,就遍历该点,并将该点放入队列,当队列第一个元素的所有相连的点都被访问过后,循环结束,再从队列中取出第一个元素,进行循环遍历。
    while (!q.empty()){ p = q.front(); q.pop(); EdgeNode * e = adjMulList[p].firstEdge; while(e) { if (e->ivex == p) { if (!visited[e->jvex]) { visited[e->jvex] = 1; //std::cout << adjMulList[e->jvex].name + " "; bfs_str.append(adjMulList[e->jvex].name.substr(0, 2) + "->"); q.push(e->jvex); } e = e->ilink; } else if (e->jvex == p) { if (!visited[e->ivex]) { visited[e->ivex] = 1; bfs_str.append(adjMulList[e->ivex].name.substr(0, 2) + "->"); q.push(e->ivex); } e = e->jlink; } }}
    分析上面算法,每个顶点至多进一次队列,遍历过程实质上仍是寻找每个顶点的邻接点的过程,因此时间复杂度和深度优先搜素一样。
    4 上机调试过程调试过程中,一方面是解决产生的bug,另一个面是调整文件中的数据,保证数据的合理性。
    点的名称及坐标

    无向图的边

    5 测试结果及其分析程序运行界面

    遍历演示


    本程序以图形化的形式演示,用户能够任意指定起始点开始遍历,遍历的结果也清晰可见。其中显示了两种遍历的城市编号的序列和遍历的边集。
    6 用户使用说明本程序是在windows7下的Qt creator5.4.1环境下编写的,因为Qt本身就是跨平台图形界面软件,所以本程序支持跨平台运行,支持的平台包括Windows、Mac OS、Linux等桌面操作系统。
    执行本程序后,窗口上回现实所有城市的名称、编号和路线,输入框中默认填写的合肥的编号,该输入框支持模糊查询,可以输入城市名称、城市编号或编号加名称。点击按钮“演示”后,会以文本形式显示遍历城市的序列,以图形化的方式,显示遍历的城市之间的连线。并且可以反复搜索遍历,具有良好的用户体验。在该可执行文件目录下的map.txt是本程序的所依赖的数据来源,文件中的数据可供用户或开发者调整修改。
    参考文献[1] 王昆仑, 李红. 数据结构与算法. 北京: 中国铁道出版社, 2006年5月
    [2] Kay_Sprint. 邻接多重表存储无向图以及有关操作[EB/OL]. http://www.2cto.com/database/201111/110964.html, 2011-11-13/2017-2-16
    1 评论 21 下载 2018-10-31 11:48:00 下载需要9点积分
  • 基于C++实现的运动会统分系统

    一、需求分析本系统主要是运动会分数统计方案设计。运动会分数统计方案适合采用结构体数组,为了实现系统功能,主要应实现以下几部分:比赛成绩输入、比赛成绩输出、查询比赛成绩和调用统计结果,进入菜单界面后,需要输入学校编号,项目编号,取得的名次个数,以及哪些名次,并且应该提供键盘式选择菜单实现功能选择。由于运动会分数统计需要处理大量的数据,所以在运行期间,为了避免在运行大量数据时出错,并且系统能够在很短的时间内将运行结果稳定准确输出,就需要系统达到安全性能好,可靠性高,稳定性强,处理数据迅速等特点。
    程序执行命令包括:

    设置运动会相关参数
    输入比赛项目名称和学校名称
    输入各学校比赛成绩以及学校成绩的查询操作

    测试数据:

    二、概要设计按照课题要求,在设计时将本系统分为输入项目成绩、查看学校成绩、查看项目成绩、输入学校和项目名称、设置几个功能模块,并且将对录入的分数按照各项成绩得分以及团体总分排序。系统定义数据时使用结构体和结构体数组来存储信息数据,输入基本信息后由系统统计总分的内容并全部存入文件file中,在排序输出中使用归并排序进行不同关键字的排序,查询函数采用顺序表的查找来完成。
    2.1 学校的抽象数据类型ADT School{ 数据对象:D={ai|ai(-SchoolSet,i=1,2,3,…,n,0<n<=20} 数据关系:R1={<ai-1,ai>|ai-1,ai(-D,i=2,3,…n} 基本操作: firstUsed(&S) 操作结果:将学校结构体中的数据置空,清零。 saveToFile (&S) 操作结果:将学校结构体中的数据存储到文件中。 readFromFile(&S) 操作结果:将文件中的学校结构体数据读取到内存中。 merge_sort($S,option) 操作结果:将学校结构体数组按照条件排序。 schoolInput(&S) 操作结果:将学校信息输入到学校结构体中。 showSchool(&S) 操作结果:将学校结构体中的数据输出。}ADT School
    2.2 比赛项目的抽象数据类型ADT Sport{数据对象:D={ai|ai(-SportsSet,i=1,2,3,…,n,0<n<=20} 数据关系:R1={<ai-1,ai>|ai-1,ai(-D,i=2,3,…n} 基本操作: firstUsed(&S) 操作结果:将比赛项目结构体中的数据置空,清零。 saveToFile (&S) 操作结果:将比赛项目结构体中的数据存储到文件中。 readFromFile(&S) 操作结果:将文件中的比赛项目结构体数据读取到内存中。 inputScores (&S) 操作结果:将比赛项目信息输入到学校结构体中。 showSports (&S) 操作结果:将比赛项目结构体中的数据输出。}ADT Sport
    由于本程序中的学校和比赛项目数据类型呈平行关系且他们在程序运行期间反复使用,故设计为全局变量,减少传参导致系统性能的降低;又由于二则的操作几近相同,故把二者的操作放在一起,同时执行,因此,抽象数据类型的设计与代码优化后的结果略有差异。
    2.3 本程序包含六个模块2.3.1 主程序模块Void main(){ 初始化; While(1){ 接收命令; 处理命令; If(退出)break;}
    2.3.2 输入项目成绩模块实现对运动会比赛成绩的输入操作。
    2.3.3 查看学校成绩模块实现对学校的比赛成绩的排名查看。
    2.3.4 查看项目成绩模块实现对项目的成绩查看。
    2.3.5 输入学校和项目名称模块实现对学校和比赛项目的名称输入。
    2.3.6 输入学校和项目名称模块实现对学校和比赛项目的名称输入。
    2.4 模块调用图
    三、具体设计3.1 头文件声明和全局变量定义#include <stdio.h>#include<windows.h>#include<stdlib.h>#include<conio.h>#include<string.h>#define default_num 3 ///默认获奖名次数目,取3或5int n=20; ///最大学校数目int m=10; ///最大男子项目数目int w=10; ///最大女子项目数目School schools[25]; ///学校结构体数组School temp[25]; ///用来缓存排序列表学校数据School temp1[25]; ///排序缓存数组Sport sports[50]; ///比赛项目结构体数组int three[3]={5,3,2}; ///获得前三名的学校积分int five[5]={7,5,3,2,1}; ///获得前五名的学校积分
    3.2 函数声明///从数据文件读取存有的运动会成绩信息void readFromFile(); ///将输入到内存中的数据存到文件中void saveToFile(); ///程序文件数据清零void firstUsed(); ///初始化操作,在此文件操作,进行打开程序的读取初始化操作void initialization(); ///按照不同的参数对学校数组进行排序,采用归并排序void merge_sort(School temp[],int st,int en,int option,School temp1[]); ///输入学校成绩并统计void schoolInput(int schoolnum,int award_num,int i,int pos); ///输入项目成绩菜单void inputScores(); ///输出学校菜单void showSchool(); ///输出查找项目菜单void showSports(); ///输入学校和项目名称菜单void InputName(); ///设置菜单void setting(); ///主菜单的操作void menu();
    3.3 数据结构定义3.3.1 运动项目数据表运动会系统先制定本次运动会所需的参赛项目。本数据表根据要求设计存储每个项目的编号、项目名称、要取的名次、各个名次获奖学校。用于对以后项目情况的统计已及查询。其中项目编号直接用运动项目数组下标+1表示,name和win_school由输入信息输入,award_num由设置选项输入。
    typedef struct Sport{ char name[20]; ///项目名称 int win_school[5]; ///获奖前三或前五学校的编号 int award_num; ///这个运动项目取名次的数目,取3或5} Sport; ///存放项目信息Sport sports[50]; ///定义运动项目数组
    3.3.2 学校数据表本数据表根据要求储存了各个参赛学校的总体情况,包括学校的编号、学校名称、各个项目得分、学校总分、男子团体总分、女子团体总分。其中学校编号由学校数组下标+1表示,学校名称,各项目得分由输入信息输入,学校总分,男子团体总分,女子团体总分由系统自动统计。
    注:学校总分,男子团体总分,女子团体总分存储在一个长度为3 的数组当中,作此设计是为了实现对学校的按照特定参数排序操作中,用传入一个下标参数的形式实现对排序条件的控制。
    typedef struct School{ int total_score[3];///下标0表示学校总分,1表示男子总分,2表示女子总分,方便之后的排序输出而存在数组中 char name[20]; ///学校名称 int scores[50]; ///各个项目得分} School;School schools[25]; ///定义学校数组
    四、运行结果4.1 主菜单界面
    4.2 设置运动会相关参数
    4.3 输入学校和项目名称
    4.4 输入运动会比赛成绩
    4.5 查看学校运动会成绩
    4.6 查看项目比赛成绩
    五、调试分析
    学校数组和比赛项目数组的数据一开始出现不同步的现象,两个数组之间的数据没有对应关系,这是导致一开始程序运行结果与预期结果不一致的主要问题
    对于数据文件的储存,刚开始没有考虑程序执行一半退出系统前要先把数据保存再退出,导致操作数据丢失。对于文件操作,文件的内容及时保存是避免这个错误的可靠方法
    本程序的模块跳转之间的文件保存、跳转之间的数据传递、模块跳转的合理性也是一开始设计上不足所在,这些问题经过调试也得到解决,因此,在以后的设计中,应该要把细节方面考虑清楚,减少对问题的考虑疏忽导致程序产生难以找到的错误
    关于中英文的字符串长度和对齐问题:经过调试得知中文一个字符的长度是2,这在学校成绩输出表格的对齐问题上有重要作用
    程序代码的时空分析:由于本程序中的学校数组和比赛项目数组长度都是常数阶,故本程序的时间复杂度为O(1),空间复杂度也为O(1)

    六、程序数据的文件存储形式本程序将程序数据分为三个txt文件保存,分别是schools.txt, sports.txt, setting.txt。分别保存学校数组,运动项目数组和设置参数的数据。其中学校数组和运动项目数组按照结构体的成块储存,每块保存着单个结构体的所有参数数据,而设置参数则按照设置菜单的顺序直接存储各个参数。该储存结构可通过附件查看得到。
    七、关于非法数据的输入在本程序中,不论整体非法数据还是局部非法数据,在数据的输入过程中对数据直接进行检测,只要遇到一个非法数据,即提醒用户重新输入,知道输入的数据合法为止,例如以下情况:

    八、用户使用说明本程序的运行环境为Code::Blocks13.12,执行文件为SportsMeeting.exe,在进入该程序软件打开文档,然后对本程序进行调试,调试完毕,用户可根据需求从主菜单选择相应的功能,调用功能函数对用户需求进行单独运算。
    本程序通过对实际操作的考虑,设计成在程序执行前先进行对运动会相关参数的设定,再输入学校、项目名称,最后再输入成绩进行分数统计,其中,参数设定必须先执行,之后的操作可由用户喜好决定。
    1 评论 19 下载 2018-10-31 11:14:43 下载需要7点积分
显示 780 到 795 ,共 15 条
eject