zoukankan      html  css  js  c++  java
  • 201871010134-周英杰 实验三 结对项目—《D{0-1}KP 实例数据集算法实验平台》项目报告

    项目 内容
    课程班级博客链接 https://edu.cnblogs.com/campus/xbsf/2018CST/homework/11903
    这个作业要求链接 https://www.cnblogs.com/nwnu-daizh/p/14604444.html
    我的课程学习目标 体验软件项目开发中的两人合作,练习结对编程;掌握Github协作开发程序的操作。
    这个作业在哪方面帮助我实现学习目标 通过学习《现代软件工程—构建之法》第3-4章内容,我理解了代码风格规范、代码设计规范、代码复审、结对编程等的概念;提高了软件开发能力、与别人合作的能力,以及学习工具的使用也是有所提高。
    对方学号—姓名 201871010133—赵永军
    对方本次博客作业链接 https://www.cnblogs.com/zhaoyongjun0911/p/14619971.html
    本项目Github的仓库链接地址 https://github.com/zhaoyongjun0911/D-0-1-KP-
    • 实验目的与要求:
      (1)体验软件项目开发中的两人合作,练习结对编程(Pair programming)。
      (2)掌握Github协作开发程序的操作方法。
    • 实验内容和步骤:

    * 任务1:阅读《现代软件工程—构建之法》第3-4章内容,理解并掌握代码风格规范、代码设计规范、代码复审、结对编程概念。
    

    (1)代码风格规范:代码风格的原则是:简明,易读,无二义性。
    (2)代码设计规范:代码设计规范不光是程序书写的格式问题,而且牵涉到程序设计、模块之间的关系、设计模式等方方面面,这里又有不少内容与具体程序设计语言息息相关(如C、C++、Java、C# ),但是也有通用的原则。
    (3)代码复审:看代码是否在代码规范的框架内正确地解决了问题。
    (4)结对编程概念:是一种敏捷软件开发的方法,两个程序员在一个计算机上共同工作。一个人输入代码,而另一个人审查他输入的每一行代码。输入代码的人称作驾驶员,审查代码的人称作观察员(或导航员)。两个程序员经常互换角色。


    * 任务2:两两自由结对,对结对方《实验二 软件工程个人项目》的项目成果进行评价。
    

    (1)对结对伙伴进行评论

    (2)阅读并测试运行结对伙伴代码
    项目的开发者:赵永军
    项目的复审者:周英杰

    提出问题 执行情况
    概要部分
    代码符合需求和规格说明么? 代码符合需求和对反说明。
    代码设计是否考虑周全? 考虑周全。
    代码可读性如何? 可以顺利阅读。
    代码容易维护么? 不容易维护。
    代码的每一行都执行并检查过了吗? 是的,可执行。
    设计规范部分
    设计是否遵从已知的设计模式或项目中常用的模式? 遵从。
    代码有没有依赖于某一平台,是否会影响将来的移植(如Win32到Win64)? 没有,不会影响移植,任何平台都可以。
    开发者新写的代码能否用已有的Library/SDK/Framework中的功能实现?在本项目中是否存在类似的功能可以调用而不用全部重新实现? 可以用;存在,有些代码是可以调用的
    有没有无用的代码可以清除?(很多人想保留尽可能多的代码,因为以后可能会用上,这样导致程序文件中有很多注释掉的代码,这些代码都可以删除,因为源代码控制已经保存了原来的老代码) 有。
    代码规范部分
    修改的部分符合代码标准和风格么? 修改的部分不符合代码标准和风格。
    具体代码部分
    有没有对错误进行处理?对于调用的外部函数,是否检查了返回值或处理了异常? 对错误都进行了处理,没有异常。
    参数传递有无错误,字符串的长度是字节的长度还是字符(可能是单/双字节)的长度,是以0开始计数还是以1开始计数? 无错误;本项目中是以0开始计数。
    边界条件是如何处理的?switch语句的default分支是如何处理的?循环有没有可能出现死循环? switch语句的default分支返回false,没有出现死循环。
    有没有使用断言(Assert)来保证我们认为不变的条件真的得到满足? 无。
    对资源的利用是在哪里申请,在哪里释放的?有没有可能导致资源泄露(内存、文件、各种GUI资源、数据库访问的连接,等等)?有没有优化的空间? 都在内存中完成,很有可能泄露
    数据结构中有没有用不到的元素? 有。
    效能
    代码的效能(Performance)如何?最坏的情况如何? 达到了具体任务的要求。
    代码中,特别是循环中是否有明显可优化的部分(C++中反复创建类,C#中 string 的操作是否能用StringBuilder 来优化)? 没有。
    对于系统和网络调用是否会超时?如何处理? 目前没有出现超时的现象。假如出现了我们会杀毒;整理系统,减少运行的进程,释放内存、cpu,释放c盘空间。
    可读性
    代码可读性如何?有没有足够的注释? 可以顺利读取;代码有足够的注释让我们读懂
    可测试性
    代码是否需要更新或创建新的单元测试?针对特定领域的开发(如数据库、网页、多线程等),可以整理专门的核查表。 可以继续开发,摆脱传统的命令行方式,更为实用。

    * 任务3∶采用两人结对编程方式,设计开发一款D{0-1}KP实例数据集算法实验平台,使之具有以下功能:  
    

    (1)平台基础功能:实验二任务3;
    (2)D{0-1}KP实例数据集需存储在数据库;
    (3)平台可动态嵌入任何一个有效的D{0-1}KP实例求解算法,并保存算法实验日志数据;
    (4)人机交互界面要求为GUI界面(WEB页面、APP页面都可);
    (5)查阅资料,设计遗传算法求解D{0-1}KP,并利用此算法测试要求((3);
    (6)附加功能:除(1)- (5)外的任意有效平台功能实现。

    • 需求分析陈述
      设计实现人机交互界面;查阅相关资料,运用遗传算法求解 D{0-1}KP;将D{0-1}KP 实例数据集存储在数据库;平台可动态嵌入任何一个有效的D{0-1}KP 实例求解算法,并保存算法实验日志数据。
    • 软件设计说明
      • D{0-1}KP数据可以保存到数据库,也可以从数据库中清除;
      • 绘制任意一组D{0-1}KP数据以重量为横轴、价值为纵轴的数据散点图;
      • 对任意一组D{0-1}KP数据按项集第三项的价值:重量比进行非递增排序;
      • 用户能够自主选择动态规划算法、回溯算法求解指定D{0-1} KP数据的最优解和求解时间(以秒为单位);
      • 对任意一组D{0-1} KP数据的最优解、求解时间和解向量;
      • 后台编写遗传算法求解D{0-1}KP。
    • 软件实现及核心功能代码展示
      • 遗传算法介绍
        • 1、编码:问题可以表示为一个n位的二进制码,第i位表示物品i,数值为0表示物品没有选中,1表示选中物品。
        • 2、种群:种群是个体的集合。
        • 3、适应度:适应度在0-1背包问题中表示的是背包中的总价值的大小,总价值越大个体的适应度越大。
        • 4、选择:从种群中选出部分个体之后进行重组或交叉,产生的新个体依据适应度函数进行优胜劣汰,选出优良个体。
        • 5、交叉:同的个体之间随机的进行杂交,其基因进行再次重组。其后产生新的具有不同适应度的新个体。
        • 6、变异:变异指单个个体的基因按照小概率进行变化的方法。本题中采用的变异方法随机产生变异点,并进行了以下两种变异类型。
          (1)变异点进行0和1的状态翻转。此方法趋于常规,但无甚缺点。
          (2) 变异点变为1,如果超出背包容积,则变为0。此方法更易产生较大的解,但如若初始种群产生的不好,则随后的变异绝不会产生最优解。
    • 核心代码
      (1)回溯算法
    class Solution:
        def permute(self, nums):
            """
            :type nums: List[int]
            :rtype: List[List[int]]
            """
            def backtrack(first = 0):
                # if all integers are used up
                if first == n: 
                    output.append(nums[:])
                for i in range(first, n):
                    # place i-th integer first
                    # in the current permutation
                    nums[first], nums[i] = nums[i], nums[first]
                    # use next integers to complete the permutations
                    backtrack(first + 1)
                    # backtrack
                    nums[first], nums[i] = nums[i], nums[first]
             
            n = len(nums)
            output = []
            backtrack()
            return output
    

    (2)动态规划算法

    def pack(w, v, n, c):
    	dp = [0 for _ in range(c+1)]
    	for i in range(1, len(w)+1):
    	    for j in reversed(range(1, c+1)):
    	        for k in range(3):
    	            if j-w[i-1][k] >= 0:
    	                # print(dp[j])
    	                dp[j] = max(dp[j], dp[j-w[i-1][k]] + v[i-1][k])
    	    # print(dp)
    	print(dp[c])
    

    (3)遗传算法

    def F(x, y):
        return 3*(1-x)**2*np.exp(-(x**2)-(y+1)**2)- 10*(x/5 - x**3 - y**5)*np.exp(-x**2-y**2)- 1/3**np.exp(-(x+1)**2 - y**2)
    
    def plot_3d(ax):
        X = np.linspace(*X_BOUND, 100)
        Y = np.linspace(*Y_BOUND, 100)
        X,Y = np.meshgrid(X, Y)
        Z = F(X, Y)
        ax.plot_surface(X,Y,Z,rstride=1,cstride=1,cmap=cm.coolwarm)
        ax.set_zlim(-10,10)
        ax.set_xlabel('x')
        ax.set_ylabel('y')
        ax.set_zlabel('z')
        plt.pause(3)
        plt.show()
        
    def get_fitness(pop):
        x,y = translateDNA(pop)
        pred = F(x, y)
        return (pred - np.min(pred)) #减去最小的适应度是为了防止适应度出现负数,通过这一步fitness的范围为[0, np.max(pred)-np.min(pred)]
    
    def translateDNA(pop): #pop表示种群矩阵,一行表示一个二进制编码表示的DNA,矩阵的行数为种群数目
        x_pop = pop[:,1::2]#奇数列表示X,从索引列1开始,加入了步长2
        y_pop = pop[:,::2] #偶数列表示y,从索引列1开始,加入了步长2
    
        #pop:(POP_SIZE,DNA_SIZE)*(DNA_SIZE,1) --> (POP_SIZE,1)
        x = x_pop.dot(2**np.arange(DNA_SIZE)[::-1])/float(2**DNA_SIZE-1)*(X_BOUND[1]-X_BOUND[0])+X_BOUND[0]
        y = y_pop.dot(2**np.arange(DNA_SIZE)[::-1])/float(2**DNA_SIZE-1)*(Y_BOUND[1]-Y_BOUND[0])+Y_BOUND[0]
        return x,y
    
    def crossover_and_mutation(pop, CROSSOVER_RATE = 0.8):
        new_pop = []
        for father in pop:		#遍历种群中的每一个个体,将该个体作为父亲
            child = father		#孩子先得到父亲的全部基因(这里我把一串二进制串的那些0,1称为基因)
            if np.random.rand() < CROSSOVER_RATE:			#产生子代时不是必然发生交叉,而是以一定的概率发生交叉
                mother = pop[np.random.randint(POP_SIZE)]	#再种群中选择另一个个体,并将该个体作为母亲
                cross_points = np.random.randint(low=0, high=DNA_SIZE*2)	#随机产生交叉的点
                child[cross_points:] = mother[cross_points:]		#孩子得到位于交叉点后的母亲的基因
            mutation(child)	#每个后代有一定的机率发生变异
            new_pop.append(child)
    
        return new_pop
    
    def mutation(child, MUTATION_RATE=0.003):
        if np.random.rand() < MUTATION_RATE: 				#以MUTATION_RATE的概率进行变异
            mutate_point = np.random.randint(0, DNA_SIZE*2)	#随机产生一个实数,代表要变异基因的位置
            child[mutate_point] = child[mutate_point]^1 	#将变异点的二进制为反转
    
    def select(pop, fitness):    # nature selection wrt pop's fitness
        idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
                               p=(fitness)/(fitness.sum()) )
        return pop[idx]
    
    def print_info(pop):
        fitness = get_fitness(pop)
        max_fitness_index = np.argmax(fitness)
        print("max_fitness:", fitness[max_fitness_index])
        x,y = translateDNA(pop)
        print("最优的基因型:", pop[max_fitness_index])
        print("(x, y):", (x[max_fitness_index], y[max_fitness_index]))
    
    if __name__ == "__main__":
        fig = plt.figure()
        ax = Axes3D(fig)
        plt.ion()#将画图模式改为交互模式,程序遇到plt.show不会暂停,而是继续执行
        plot_3d(ax)
    
        pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE*2)) #matrix (POP_SIZE, DNA_SIZE)
        for _ in range(N_GENERATIONS):#迭代N代
            x,y = translateDNA(pop)
            if 'sca' in locals():
                sca.remove()
            sca = ax.scatter(x, y, F(x,y), c='black', marker='o')
            plt.show()
            plt.pause(0.1)
            pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE))
            #F_values = F(translateDNA(pop)[0], translateDNA(pop)[1])#x, y --> Z matrix
            fitness = get_fitness(pop)
            pop = select(pop, fitness) #选择生成新的种群
        print_info(pop)
        plt.ioff()
        plot_3d(ax)
    
    • 程序运行
      • 散点图
      • 数据排序
      • 动态规划算法
      • 回溯算法
      • 遗传算法
    • 描述结对的过程,提供两人在讨论、细化和编程时的结对照片(非摆拍)。
    • PSP流程
    PSP 各个阶段 自己预估的时间(分钟) 实际的记录(分钟)
    计划: 明确需求和其他因素,估计以下的各个任务需要多少时间 2*60 3*60
    开发 (包括下面 8 项子任务) 37*60 41*60
    · 需求分析 (包括学习新技术、新工具的时间) 3*60 3*60
    · 生成设计文档 (整体框架的设计,各模块的接口,用时序图,快速原型等方法) 2*60 2*60
    · 设计复审 (和同事审核设计文档,或者自己复审) 1*60 1*60
    · 代码规范 (为目前的开发制定或选择合适的规范) 1*60 1*60
    · 具体设计(用伪代码,流程图等方法来设计具体模块) 3*60 4*60
    · 具体编码 25*60 25*60
    · 代码复审 1*60 2*60
    · 测试(自我测试,修改代码,提交修改) 1*60 3*60
    报告 1*60 2*60
    测试报告 1*60 2*60
    计算工作量 (多少行代码,多少次签入,多少测试用例,其他工作量) 1*60 2*60
    事后总结, 并提出改进计划 (包括写文档、博客的时间) 2*60 3*60
    总共花费的时间 (分钟) 44*60 53*60

    小结感受:
    结对编程绝对是1+1>2的;用到问题一定要及时提出来,共同解决,这是提高效率很好的办法,也是结对编程的核心所在;结对编程的过程中一定要注意代码规范问题,对于后期的修改复审都带来来很大的便利。

  • 相关阅读:
    HDU 2955 Robberies(01背包)
    HDU 2602 Bone Collector(01背包)
    HUST 1352 Repetitions of Substrings(字符串)
    HUST 1358 Uiwurerirexb jeqvad(模拟解密)
    HUST 1404 Hamming Distance(字符串)
    HDU 4520 小Q系列故事――最佳裁判(STL)
    HDU 2058 The sum problem(枚举)
    【破解】修改程序版权、添加弹窗
    HDU 1407 测试你是否和LTC水平一样高(枚举)
    HDU 1050 Moving Tables(贪心)
  • 原文地址:https://www.cnblogs.com/dlzyj/p/14651459.html
Copyright © 2011-2022 走看看