软工_个人项目总结博客

项目预先规划



  在个人项目开始之前,我之前的编程几乎从来没有在开始前预估过时间,所以也就是摸着石头过河,大概根据之前的经验估计了一下时间。

  预估时间(单位均为小时)


PSP 2.1


Personal Software Process Stages


Time


Planning


计划


· Estimate


· 估计这个任务需要多少时间


0.1


Development


开发


· Analysis


· 需求分析 (包括学习新技术)


1


· Design Spec


· 生成设计文档


0.5


· Design Review


· 设计复审 (和同事审核设计文档)


0.5


· Coding Standard


· 代码规范 (为目前的开发制定合适的规范)


1


· Design


· 具体设计


2


· Coding


· 具体编码


5


· Code Review


· 代码复审


1


· Test


· 测试(自我测试,修改代码,提交修改)


3


Reporting


报告


· Test Report


· 测试报告


2


· Size Measurement


· 计算工作量


0.2


· Postmortem & Process Improvement Plan


· 事后总结, 并提出过程改进计划


1


合计


17.3

  在估计完时间后,我的内心是崩溃的,完全没想到每部分两三小时的工作加起来竟然需要将近20个小时来完成,不过更没让我想到的是,竟然最后实际花去的时间还要远远大于我最开始的估计。

 

  实际时间(单位均为小时)


PSP 2.1


Personal Software Process Stages


Time


Planning


计划


· Estimate


· 估计这个任务需要多少时间


0.1


Development


开发


· Analysis


· 需求分析 (包括学习新技术)


2


· Design Spec


· 生成设计文档


1


· Design Review


· 设计复审 (和同事审核设计文档)


1


· Coding Standard


· 代码规范 (为目前的开发制定合适的规范)


2


· Design


· 具体设计


3


· Coding


· 具体编码


12


· Code Review


· 代码复审


2


· Test


· 测试(自我测试,修改代码,提交修改)


6


Reporting


报告


· Test Report


· 测试报告


2


· Size Measurement


· 计算工作量


0.2


· Postmortem & Process Improvement Plan


· 事后总结, 并提出过程改进计划


3


合计


34.3

性能改进过程



  从上面两个表格可以看出,最终我完成这个个人项目所用的时间几乎达到了计划值的两倍之多。对于设计-实现-测试这个流程来说,主要比计划多出来的时间都花在了设计与实现上,我认为之所以多花了将近一倍的时间,我想这与我从最开始设计程序开始,就在不停的思考和推翻一个个设计,并不断改进追求程序性能的最大化有关。

  如果说在做这个个人项目之前,我把编程的重点放在把需求尽可能完整的而又没有错误的实现上的话,这次贯穿整个项目流程的主线则是不断的优化算法,以寻求程序的性能在保证没有错误的基础上最大化。

  下面讲一下我整个优化的流程

  第一阶段 设计阶段

  在设计之初,我就在一直想一个问题:如何将地铁图抽象成为数据结构,用什么样的数据结构能够最简单,最直接,最利于算法的发挥,并且空间的开销最小。最开始,也是最自然想法就是将所有的地铁站按照线路来进行存储,每条线路中存储着所有的车站,然后再对于换乘站进行特殊处理。相信这是大多数人最开始的思路,于是我也按照这样的思路开始了设计,对于这样的数据结构,很自然的就想到了利用广搜来进行最短路径的搜索,对于完成按照换乘最少和站数最少的搜索,广搜似乎都能很好的得到结果。

  但是这时,我又转念一想,广搜在最坏情况下的时间复杂度达到了N^2,虽然这次图的节点数并不多,按照所有的地铁站来算,也就是不到300个节点,而且每个节点并不是与所有节点都有连接,而是大部分只与两个节点(非换乘站),最多与六个(三线换乘站)节点之间有连接,这又会大大降低广搜的搜索空间。这样的算法对于这样的数据规模还是比较合适的。

  但是,对着地铁图琢磨了一会以后,我又有了一个新的想法。

  问题就在于这地铁图的特殊性,一个站貌似与两个节点之间有连接,但实际上在地铁线路中旅行时,一个最短线路绝对不会走回头路,也就是说实际上在单独的一条线路上,最短路径只能是单向行走,直到出现了换乘站,不同的路径之间才会在这里产生分歧,走向不同的方向。所以,地铁线路中看似有非常非常多的地铁站点,但实际上能够对最短路径产生影响的,只有那些多线交叉的换乘站,而换乘站与换乘站之间的非换乘站,只要把它们抽象成一个带权值的边就足够了。在数据结构中,真正需要的,就是那些换乘站组成的换乘节点。

  按照这样的思路,整个地图就能够被简化为将近五十个换乘节点,和换乘节点之间连接的带权边,在这样的图中进行广搜,搜索效率应该能够有所提升。所以,在真正开始实现之前,我就明确了注重换乘站而尽量简化换乘站之间的非换乘站这样的整体设计。

  第二阶段 正式编码阶段

  于是在编码阶段,虽然为了计算起始点与终点不是换乘站的最短路径,我的程序的数据结构中仍然需要存储所有的站点以及所有的线路,但是在实际的算法实现中,并没有用到这些数据,仅仅是利用了一张代表所有换乘站之间的距离的二维数组,并找到距离起始点和终点最近的两个换乘站,进行最短路径的查找。这样是对于广搜算法的一次优化,提升了一部分程序性能。

  接下来,我又对广搜这个算法提出了疑问,对于这样的数据结构,能否有更高效的最短路径搜索算法呢?于是我又想到了迪杰斯特拉算法,这种算法在最坏情况下的时间复杂度要比广搜小了一个数量级,并且之前我就选定好的数据结构非常适合迪杰斯特拉算法算法的应用,因此我觉得要用迪杰斯特拉算法来提升整个程序的性能。

  然后就出现了一个小问题,因为所有按照换乘站来存储的数据结构实际上把所有的线路都割裂成了换乘站之间的小段线路的集合,也就是说在经过这样的抽象以后,很难再在寻找最短路径时区分一条边和另一条边是不是同一条路线,至少,对于迪杰斯特拉算法算法来说很难,这也就使得迪杰斯特拉算法算法无法完成对于换乘最少的最短路径的搜索。而此时广搜就可以发挥它的用武之地,在搜索时,可以配合线路图,做“线路优先”的搜索,也就是说将最少换乘最短路线问题,转换成在最少的节点的连接下,由起点线路换乘到终点线路。这是相对容易实现的。

  所以在最后的程序当中,对于不同的搜索模式,我分别用了两种不同的搜索算法。在搜索两站之间换乘最少的最短路径时,程序使用广度优先搜索,在搜索站数最少的最短路径时,程序使用迪杰斯特拉算法进行搜索。

  程序性能分析图如下,在分析性能时,我分别使用广搜和迪杰斯特拉算法对十对不同的起点终点信息进行了测试,测试结果如下

  其中BFSPath是广搜算法,DjistraPath是迪杰斯特拉算法。可以看到,在相同的测试数据下,迪杰斯特拉算法所用的时间大概是广搜的25%左右,而在更详细的分类下,广搜算法所有时间都用在了广搜的递归调用子函数上,而迪杰斯特拉算法中真正计算路径的算法部分只占用了2%左右的时间,而其他时间则用在了处理地图数据和建立路径上。可见,如果不考虑算法的复数函数的时间,两种算法在计算路径的时间上几乎有十倍左右的差距。

  而这一切的一切,还是建立在两种算法都对地铁站本身的数据结构进行了优化存储,仅保留换乘的节点的基础之上得到的数据。可想而知,如果直接使用我最开始设想的数据结构,那么用的时间还会延长。

  第三阶段 性能优化阶段

  在整体编码忘了之后,我跟根据性能分析工具提供的结果检查了一遍所有函数,找到了几个耗时间比较长的函数,发现他们的共性就是都存在调用函数时遍历数组的情况。我对此进行了分析,发现有一个函数为了返回当前线路中的总站数而遍历了整个线路集合。这是一个可以优化的点,因为集合的总站数可以在每次集合中添加或者删除元素时记录在对象中的一个整型变量中而没有必要每次都遍历整个数组。因此我对此进行了修改,并且将几个函数中关于数组遍历的部分都检查了一遍,并修改了所有类似的部分。

  在修改后,这几个函数都不在存在于耗时较多的函数列表中了。

十组测试用例



  1.MetroSystem.exe -b 2号航站楼 3号航站楼

  2.MetroSystem.exe -b 健德门 火器营

  3.MetroSystem.exe -b 南邵 角门西

  4.MetroSystem.exe -b 朱辛 马泉营

  5.MetroSystem.exe -b 西直门 国贸

  6.MetroSystem.exe -c 3号航站楼 西局

  7.MetroSystem.exe –c 北苑路北 安立路

  8.MetroSystem.exe -c 广渠门外 国贸

  9.MetroSystem.exe -c 大屯路东5 大屯路东15

  10.MetroSystem.exe -c 什刹海 什刹海

  第一组测试用例测试单行线

  第二组测试用例测试环线

  第三组测试用例测试尽头站到换乘站

  第四组测试用例测试错误输入不存在的车站

  第五组测试用例测试换乘站到换乘站

  第六组测试用例同样测试单行线

  第七组测试用例测试不能换乘的大屯路东站

  第八组测试用例测试不能换乘的双井站

  第九组测试用例测试两条线同名车站实际不能换乘的情况

  第十组测试用例测试同名车站

精益求精



  在这张地铁图中,有一条线和两个站的情况比较特殊。

  一条线就是机场线,机场线在三号航站楼和二号航站楼区间内单向运行,我的程序考虑到了这一点,对其进行了特殊处理。

  两个站则是大屯路东站和双井站,两站看似5和15号、7和10号线在此交叉,实际上两线之间的换乘通道并未开通,也就是两条线的同名站实际上是两个独立的站点,彼此之间并不能互相换乘。在我的程序中,用大屯路东5和大屯路东15,双井7和双井10来区分这两个车站。

附加题的思路



  对于附加题,我有一些想法。

  首先,我认为,无论从哪个站点出发,最短环形路线都应该是一样长的。这个很简单就能证明,因为每个站至少都经过一次。因此,对于这张地铁线路图,完全不需要每次都对这个路线进行计算,而是把这个计算得到的最短路线储存起来,从各个站出发的路线只需把环形的起始位置稍加改变即可,站与站之间的相互位置是保持不变的。

  所以这样一来,这个问题就成了在一个无向图之中求最短可重复边的回路的问题,而这个问题又叫做中国邮递员问题,已经有相当多的算法可以解决这个问题。但是限于时间所限,我没有能够将这个算法写到我的程序之中,不过相信有了这个思路,实现起来应该并不需要多长的时间了。

  最后我甚至还有一个比较有意思的算法,因为求中国邮递员问题就是要将当前所有度为奇数的节点之间加入最短的重复边,使得所有节点的度都为偶数,而北京地铁中度为奇数的节点并不多,这个计算量甚至可以直接在纸上算出来最短回路,然后直接保存于程序中,然后根据起始点的不同进行调整。虽然这种方法只能对应当前的地铁图,但是鉴于地铁图的更新频率并不高,这样的做法看起来也是可以接受的。

个人项目总结



  虽然这并不是我第一次编写将近1000行的程序,但是这次编程是我第一次编写一个可以称之为项目的程序。相比于从前,我投入了更多的时间在设计和优化上,不再像以前一样仅仅满足于写出一个正确的程序,而是更多的希望能够写出一个高性能的,优美的程序。虽然这样的目标现在看来还远远没有达到,但是从这次项目之中,我学到了很多。

  首先我学到了提前规划,一个好的设计,不仅能够让之后的编码过程更加逻辑清晰,还能让代码的效率提高不少。之前虽然也会提前设计,但是也就是想想需要用什么算法,如果能差不多在大脑里面有了一个可用的算法能够解决这个问题,我一般就差不多开始动手写了。可是根据这次项目的经验,最先跳出脑海的算法往往都是我们最熟悉的算法,其实这是不太好的。首先,熟悉而常见的算法效率不一定是最高的,其次往往忽视了对于问题的简化和深入思考,最后,会导致知识仅仅局限在那几个算法中绕圈,没有尝试也就没有学习与提高。

  其次,我学到了优化。之前,我从来没有接触过对于性能方面的分析,也不知道VS竟然有如此方便的工具能够自动生成报表,来告诉我们那些占用时间比较长的函数,来方便我们进行优化。这次学到了一点优化性能的皮毛,现在看来,从最开始的设想到最后的实现,大概将性能提升了十倍左右。这是一件很让人有成就感的事情。

  最后,我的调试技术又更进了一步。这次编程中,第一次运行程序免不了会出现各种各样的Bug,利用VS功能强大的调试工具,结合断点和分部调试,并适当在程序中加入判断语句以便更好的定位断点和出错点,出现的Bug基本是一次性找出,一次性全部解决,这比之前经常苦恼找不到问题出在哪里有了很大的提高和进步。

附:GitHub源码地址

https://github.com/shihaoran

时间: 2024-10-04 22:22:16

软工_个人项目总结博客的相关文章

软工_结对项目总结博客

关于结对编程 第一次进行真正的结对编程,而且我们组又是最奇葩的三人组合(14061183韩青长)(14061195陈彦吉),在经历了三天的合作以后,感觉收获还是蛮多的,下面是我对于结对编程的一些个人体验. 优点 在结对编程的过程中,两个人共同面对同一份代码,编码时旁边时刻有人提示监督.这样写出的代码,首先考虑的特殊情况会更多,能避免很多一个人编程时因为考虑不周而在某个不起眼的地方产生的Bug,代码质量更高,少了很多调试时间. 同时,由于两个人交替工作,一方面可以缓解疲劳,同时又因为身旁有人共同工

[福大软工] Z班——个人技术博客评分

个人技术博客 作业地址 https://edu.cnblogs.com/campus/fzu/SoftwareEngineering2015/homework/1070 作业要求 个人技术博客单次作业满分为10分,博客的形式与内容不做任何限制,但要在博客中说明博客所介绍的技术与团队项目的关系.第一次个人技术博客的截止日期会定在团队阿尔法阶段结束时(初步设定了截止日期,之后会根据团队项目整体的进度调整). 注意:技术博客是个人作业,需要每位同学独立完成. 评分准则 本次技术博客的分数由两部分组成,

Beta阶段项目展示博客

Beta阶段项目展示 团队成员的简介 详细见团队简介 角色 姓名 照片 项目经理,策划 游心 策划 王子铭 策划 蔡帜 美工 赵晓宇 美工 王辰昱 开发.架构师 解小锐 开发 陈鑫 开发 李金奇 开发 杨森 团队成员个人博客地址 游心:http://www.cnblogs.com/jefhq/ 王子铭:http://www.cnblogs.com/514DNA/ 蔡帜: http://www.cnblogs.com/felixcaae/ 赵晓宇: http://www.cnblogs.com/z

南工院移动1521班博客及分组情况

南工院移动1521班第一次课程,分组并创建博客.具体内容如下:   姓名 博客地址 分组 组长标记 徐昊 http://www.cnblogs.com/xuritian317/ 1 * 张伟港 http://www.cnblogs.com/z952061617/ 1   李雪 http://www.cnblogs.com/lixue1206/ 1   张志辉 http://www.cnblogs.com/z809931330/ 1   沈犇 http://www.cnblogs.com/qq71

团队项目测评博客

第一部分 调研,评测 评测 安卓端评测 测试人:文垚 描述最简单直观的个人第一次上手体验. 第一次上手体验,操作简单,界面简洁.课程表与超级课程表差不多,不同课程不同颜色显示,简洁明了.但是整体界面在简洁中透露出些许简陋,整体UI设计缺少灵性,只有最基本的框架没有进行优化,不够美观.特别是教务通知这一版块,显示过于简陋,教务通知显示经常出现排版混乱的问题. 使用思维导图,描述福大助手的结构体系 按照描述的bug定义,找出至少两个功能性的比较严重的bug. 用专业的语言描述bug(每个bug 不少

[Alpha阶段]项目展示博客

Alpha阶段项目展示 1.团队成员介绍 bsh 负责工作:前端开发及测试,前端负责人. bsh的个人博客 byw 负责工作:PM,各类文档.博客的撰写,总体计划及督促完成进度. byw的个人博客 lqh 负责工作:后端开发及测试. lqh的个人博客 lw 负责工作:后端开发,后端负责人. lw的个人博客 szy 负责工作:暂定后端开发及测试. szy的个人博客 wb 负责的工作:前端开发. wb的个人博客 ycd 负责的工作:页面的优化与改进 ycd的个人博客 2.工程相关信息 (1)我们的用

个人项目终极博客——四则运算题目生成程序分析

四则运算题目生成程序分析 13061184 马腾跃 一.时间分析 PSP2.1 Personal Software Process Stages Time Planning 计划   · Estimate ·估计这个任务需要多少时间 15h Development 开发 · Analysis ·需求分析(包括学习新技术) 2h · Design Spec ·生成设计文档 1h · Design Review ·设计复审(和同事审核设计文档) 0.5h · Coding Standard ·代码规

第一次软工作业 个人项目 词频统计

1.预计完成时间: 在一开始的时候,我并不认为这项作业的完成难度有多大.因为觉得这个程序主要的部分就是三块码,读入当前目录下的所有内容,统计单词和排序,但是我对于C++和C#两种语言都不熟悉,所以准备先用两天来熟悉语言(后来发现这个决定是错误的..至少 不应该用这么长的时间).在程序的具体模块中,我划分了四个模块: 主函数:处理整个流程,包括读入目录中的所有内容和对于所执行模式的判断,预计用时1小时. 分割函数:将文件中的单词分割出来.由于有三种模式,预计用时3小时. 计数函数:统计频率,作为统

软工_个人博客作业1

阅读<构建之法>中的问题 关于变量名的问题 在书中我看到了在C等弱类型语言中因为变量语义多样所以需要用“匈牙利命名法”等方法来规范变量的命名.实际在我平时的编程之中,对于变量的命名也有很大的困惑,见到有些同学的拼音首字母命名法等等,我想问一下,在实际的工程运用中,对于变量命名又是怎样处理的呢?有没有一个约定俗称的规范呢?如何才能写出更加工程化的命名方式?这点在书中并没有详细说明. 关于结对编程的问题 在阅读课本之前,我脑海中对于结对编程的概念是两个人分配好任务,然后各自完成.但是在书中介绍的结