• 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
    ",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
    ",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

     

      

  • 相关阅读:
    如何撤销Git操作?
    SpringBoot Controller接收参数的几种方式盘点
    全面解析Spring中@ModelAttribute注解的用法
    Java中将字符串转为驼峰格式
    如何将Map键值的下划线转为驼峰
    JS如何获取地址栏url后面的参数?
    解决wordpress 5.3更新后Uncaught Typeerror: $ is not a function
    小程序如何判断用户(后台使用Django)
    服务器 Web服务器 应用服务器区别联系
    C语言和Python语言在存储变量方面的不同
  • 原文地址:https://www.cnblogs.com/shone/p/3380438.html
Copyright © 2020-2023  润新知