GUI、模块化与结对编程(homework-03)

摘要:

  在本次作业博客里,我将主要阐述作业3的收获。作业3表面是将之前的程序转换为图形界面(之前程序见http://www.cnblogs.com/shone/p/3348372.html),然而本质是:

1. 熟悉模块化、重构、重写 

2. 体验结对编程。

  作业以上一次的优秀代码为基础,我们首先敲定了/h, /v, /h /v, /a几个模块的算法,然后进行模块化,成为独立的.dll文件,最后使用PYQT写UI并调用前述dll文件,实现原始需求。博客中老师要求提到的内容,以加粗表示。

上图为我们的成果,并以此作为测试通过的象征,其他功能已经通过测试,不在此赘述。

1. 算法相关

  算法部分:http://www.cnblogs.com/shone/p/3348372.html 这篇博客里,我不断地使用归结法,解决了 /h, /v, /h /v问题,但是在/a中败下阵来,苦思冥想而不得解。在本期结对编程中,我请教了大神熊英夫,在他老人家的点化下,我们为 /a 问题找到了一个相对满意的算法:
  SAA退火算法。退火算法用来提供近似最优解:由初始解i和控制参数初值t开始,对当前解重复“产生新解→计算目标函数差→接受或舍弃”的迭代,并逐步衰减t值,算法终止时的当前解即为所得近似最优解,这是基于蒙特卡罗迭代求解法的一种启发式随机搜索过程。退火过程由冷却进度表(Cooling Schedule)控制,包括控制参数的初值t及其衰减因子Δt、每个t值时的迭代次数L和停止条件S。下面代码中,MaxS表示当前状态下选择的最优化状态,RanS表示一个随意拓展的状态:

 1  void SAA(int v,float T,float r,float Tmin)
 2     {
 3         int i,max,maxS=0,ranS,tmp,SMR,sum,ri,j;//State
 4         struct link * p;
 5         srand(time(0));
 6         sum=value[v];
 7         for(i=0;i<1024;i++){visited[i]=0;available[i]=0;}
 8         topq=0;p=g[v];visited[v]=1;
 9         while (p!=NULL)
10         {
11             if (!visited[p->t]){available[p->t]=1;}
12             p=p->next;
13         }
14         while(1)
15         {
16             max=-10000000;
17             ri=0;
18             for (i=0;i<num;i++)
19             {
20                 if (visited[i]) {continue;}
21                 if (available[i])
22                 {
23                     tmp=estimate(i);
24                     if (tmp>max)
25                     {
26                         max=tmp;
27                         maxS=i;
28                     }
29                     ri++;
30                 }
31             }
32             if (ri==0) {break;}
33             ranS=rand()%ri+1;
34             for (i=0;i<num;i++)
35             {
36                 if (available[i]&&!visited[i])
37                 {
38                     ranS--;
39                     if (ranS==0)
40                     {
41                         ranS=i;
42                         break;
43                     }
44                 }
45             }
46             if (visited[ranS]||!available[ranS]) {return ;}
47             SMR=0;
48             tmp=estimate(maxS);
49             if (sum+tmp>totalmax) {totalmax=sum+tmp;pseudoexpand(maxS);}
50             if (tmp>0){SMR=1;}
51             else
52             {
53                 if (exp(tmp/T)>(rand()%10000)/10000.0){SMR=1;}
54             }
55             T*=r;
56             tmp=SMR==1?estimate(maxS)-estimate(ranS):estimate(ranS);
57             if (sum+estimate(ranS)>totalmax) {totalmax=sum+estimate(ranS);pseudoexpand(ranS);}
58             if (exp(tmp/T)>(rand()%10000)/10000.0){SMR=2;}
59             T*=r;
60             switch(SMR)
61             {
62                 case 1:sum+=expand(maxS);break;
63                 case 2:sum+=expand(ranS);break;
64             }
65             if (T<Tmin){break;}
66             if (sum>totalmax) {totalmax=sum;printf("%d\n",totalmax);for (j=0;j<1024;j++){chosen[j]=visited[j];}}
67         }
68     }

  上面的代码使用了5次相同的退火过程,每个过程1000*点数V次初始温度。退火算法并不能保证是最优解。下面的patch()函数从较优解提升为最优解。patch()函数将正值的点打包,得到局部不可扩充最优解,代码:

 1  void patch()
 2     {
 3         int i,j,sum,yes;
 4         struct link * p,*q;
 5         while(1)
 6         {
 7             yes = 0;
 8             for (i=0;i<num;i++)
 9             {
10                 if (value[i]>0&&!chosen[i])
11                 {
12                     for (j=0;j<1024;j++) {visited[j]=0;}
13                     q = DFS(i);
14                     p = q;
15                     if (p==NULL)
16                     {
17                         continue;
18                     }
19                     sum = p->s;
20                     if (sum>=0)
21                     {
22                         yes = 1;
23                         while (q!=NULL)
24                         {
25                             chosen[q->t] = 1;
26                             q = q->next;
27                         }
28                     }
29                 }
30             }
31             if (yes==0) {break;}
32         }
33     }
34 
35     struct link * DFS(int v)
36     {
37         struct link * p;
38         struct link * q;
39         struct link * tmp;
40         int result=-100000;
41         p = g[v];
42         visited[v] = 1;
43         q = (struct link *)malloc(sizeof(struct link));
44         q->next = NULL;q->s = value[v];q->t = v;
45         while (p!=NULL)
46         {
47             //printf("%d %d %d\n",p->t,visited[p->t],chosen[p->t]);
48             if (chosen[p->t])
49             {
50                 visited[v] = 0;
51                 q->next = NULL;q->s = value[v];q->t = v;
52                 return q;
53             }
54             if (visited[p->t]||value[p->t]>0)
55             {
56                 p=p->next;
57                 continue;
58             }
59             if (value[p->t]<0&&!chosen[p->t])
60             {
61                 tmp = DFS(p->t);
62                 if (tmp!=NULL&&result<tmp->s+value[v])
63                 {
64                     result = tmp->s+value[v];
65                     q->next = tmp;
66                     q->s=tmp->s+value[v];
67                 }
68             }
69             p = p->next;
70         }
71         visited[v] = 0;
72         if (q->next!=NULL) return q;
73         return NULL;
74     }

  当patch()可用,我们可以直接得到状态MaxS和RanS。patch()无用时,最优解已经可以在退火过程被选取。又因为/v, /h只是图形对称的问题,到此与/a有关的问题便得到解决。

2. 模块化与重构

  模块化的意义:我们希望将作业2实现的代码独立出来。模块化能够使得之前的命令行与现在的GUI使用同一份代码,这将提高代码的重用性,提高程序开发的效率,让我们少写一点代码,多一点时间打游戏。同时,专事专做,正确率高,在一定程度上也保障了相关代码的安全。

  模块化的方式:代码模块化,可以使用class library,dll等方式。这里面,我们选择后者。

我的代码变化在哪里呢?

  主要是两方面:

1. 补充\a的算法(见前文)

2. 补充图形用户界面。因为我们的代码都是正确的,考虑到一致性的需要,当我们坐在一起编程时,便选择一人的代码为基础,共同进行改进。总体上来说,各个模块化的函数按需进行重构,涉及到文件读取、控制的代码需要重写,并与GUI结合。

3. 增加对动态链接库的调用。

重构与重写:

    这里面,原始的命令行代码分为被调用函数和main函数两部分。前者是模块化的函数,处理\a, \h等等情况,是可共用部分,进行重构,导出到dll中。而main中进行文件读取、对调用函数进行调用的与控制流程相关的代码,则需要根据GUI重写。这里面使用python实现。

3. 我使用的代码规范

  我(们)再编程中特意注意了代码规范,尽力写出可读性强、风格良好的代码。代码风格是内在问题,变化最大的是代码外观:排版与格式。

  第一方面,我全面阅读了《C语言代码规范》和老师给的博客,并按照其处理缩进、下划线,/类/函数名都用Pascal形式,所有的变量都用Camel形式。

  第二方面,由于使用Visual Assist插件,基本的代码缩进、字体、颜色等方面已经可以自动保证。
  

  近期学习到的是老师的博客,在这篇博客中,说明{}各占一行是一个更加清晰的方法,而且不论是VS还是VA插件都默认如此。我之前的习惯是{不独立占行,而}独立占行。我之前认为这样能保证代码的简洁明了。但是博客中提出了“ }else{”这样的例子,这样就不再清晰,因此,我优化了{}的使用。

  另外,注释是我忽略的地方,我明确了了什么是有用的注释,以及注释恰当的位置。

4. 结对编程

  我们实践了结对编程,结对编程中队员可以不断地彼此提醒、鼓励并进行代码复查,提高代码正确性和编程效率,从开发层次、心理感受、管理上都是很好的方法。

  我最开始的小伙伴叫熊英夫,男,我航计院人氏。后来在助教大人的允许下,我们收留了因同伴退课而无家可归的小伙伴柴泽华。柴泽华,男,我航计院人氏,人称柴哥。

  我的小伙伴们十分优秀。

优点:

熊英夫是编程的大牛,除了小地方由我们提示外,熊英夫对于算法和Python都是我们中最出色的。除此之外,他还很谦虚,很接受我们的建议,丝毫不因为技术水平高而拒绝交流。最后,他很肯干,写了大比例的代码。

柴哥是个比较搞笑的人,这算一个优点吧。除此之外,他经常提醒我们的进度,并且经常和我交流博客的写法问题。

小伙伴们需要改进的地方:

我们不得不承认谁都有可以改进的地方。首先,我们三人的编程水平都是可以增强的,尤其是\a问题上也是费了很大劲才得到结论。其次,我们对于代码重构的知识了解甚少,做得时候并不十分专业和流畅。第三,我们都有拖延症,不到deadline不提交...

整个过程中,我从小伙伴们学到的最大的收获是退火算法,当然我也发挥了我的价值,比如使用两种语言分别处理算法部分和UI部分。


你的设计是如何保证 不同的 maxsum.exe 命令行最后在一个GUI 的界面显示的? (C++ 的设计模式中有 singleton 的概念, 说明一个类的实例如何在一个进程中保持单例, 我们这里谈的是软件如何在操作系统中保持 singleton)

这样的功能,可以通过建立管道来实现,程序端监听,命令行向程序发送消息,若程序无标签,则新建并显示;若已经有标签,则在其右侧显示出一个标签出来。

但是我们在学习导出dll,调用dll,以及退火算法上耗费了大量的时间,上述的思想已经不再有时间实现..于是我们直接显示了file1和file2两个标签,抛弃命令行,直接在GUI编辑和运行。这里的实现方式是:在UI部分相对简单的前提下(运行效率要求不高),使用PYQT,先使用ERIC4提供的QTdesigner绘制框架再通过ERIC4向python编译,再用自己写的窗口类继承setui。

5.表格

 

 

 

Personal Software Process Stages

时间百分比(%)

实际花费的时间 (分钟)

原来估计的时间 (分钟)

Planning

计划

 

 

·         Estimate

·         估计这个任务需要多少时间,把工作细化并大致排序

 100

 30 

 30

Development

开发

 80

 

 

·         Analysis

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

 200

 120

 60

·         Design Spec

·         生成设计文档

 0

 0

·         Design Review

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

 同时进行

 

 

·         Coding Standard

·         代码规范 (制定合适的规范)

 同时进行

 

 

·         Design

·         具体设计

 

 

 

·         Coding

·         具体编码

 100

 180

 180

·         Code Review

·         代码复审

 同时进行

 

 

·         Test

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

 100

 30

 30

Reporting

总结报告

 

 

 

·         Test Report

·         测试报告

 

 

 

·         Size Measurement

·         计算工作量

 100

 20

 20

·         Postmortem & Improvement Plan

·         事后总结, 并提出改进

 100

 60

 60

Total

总计

 

总用时 440

总估计的用时 500

 

  

posted @ 2013-10-21 13:32  Shone JIN  阅读(398)  评论(1编辑  收藏  举报