• 软件工程第二次作业


    博客信息 沈阳航空航天大学计算机学院2020软件工程作业
    作业要求 https://edu.cnblogs.com/campus/sau/Computer1701-1705/homework/10583
    课程目标 熟悉一个“高质量”软件的开发过程
    作业目标 单元测试练习

    一、题目要求

    问题:给定n个整数(可能为负数)组成的序列a[1], a[2], a[3], …, a[n], 求该序列如a[i] + a[i + 1] + … + a[j]的子段和的最大值。
    当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为:Max{ 0,a[i] + a[i + 1] + … + a[j] }, 1 <= i <= j <= n。
    例如:当(a[1], a[2], a[3], a[4], a[5], a[6]) = (-2, 11, -4, 13, -5, -2)时,最大子段和为20。

    解决方法

    1. 暴力解法
      sum[i..j]为数组中第i个元素到第j个元素的和(其中0<=i<j<=n-1),通过遍历所有的组合之和,就能找到最大的一个和了
    #include "stdafx.h"
    //暴力法求最大子数组和问题
    int _tmain(int argc, _TCHAR* argv[])
    {
        int A[8] = { -6, 10, -5, -3, -7, -1, -1 };
        int array_length = sizeof(A) / sizeof(A[0]);//数组大小
        int sum = -10000;//记录子数组的和
        int low;//记录子数组的底
        int height;//记录子数组的高
        for (int i = 0; i < array_length; i++)
        {
            for (int j = i ; j < array_length; j++)
             {
                int subarraysum=0;//所遍历出来的子数组的和
                //计算遍历的子数组之和
                for (int k = i; k <= j; k++)
                {
                   subarraysum += A[k];
                 }
                 //找出最大的子数组
                if (subarraysum>sum)
                 {
                    sum = subarraysum;
                    low = i;
                    height = j;
                 }
            }   
    }
    printf("%d  %d  %d", low, height,sum);//将结果打印出来
    getchar();
    return 0;
    }
    

    显而易见,此程序的时间复杂度为O(n3),所以不推荐这种。

    2.分治法
    我们把数组A[1..n]分成两个相等大小的块:A[1..n/2]和A[n/2+1..n],最大的子数组只可能出现在三种情况:
    A[1..n]的最大子数组和A[1..n/2]最大子数组相同;
    A[1..n]的最大子数组和A[n/2+1..n]最大子数组相同;
    A[1..n]的最大子数组跨过A[1..n/2]和A[n/2+1..n]
    前两种情况的求法和整体的求法是一样的,因此递归求得。
    第三种,我们可以采取的方法也比较简单,沿着第n/2向左搜索,直到左边界,找到最大的和maxleft,以及沿着第n/2+1向右搜索找到最大和maxright,那么总的最大和就是maxleft+maxright。
    而数组A的最大子数组和就是这三种情况中最大的一个。
    伪代码如下:

      #include "stdafx.h"
       //分治法求最大子数组和问题
      struct PositioASum {
         int low;
         int high;
         int sum;
      };
      //寻找包含中点位置的最大子数组函数
      PositioASum MaxCrossingSubarray(int a[], int low, int mid, int high)
      {
        //求中点左边的最大值和最大位置
        int maxLeft;//记录左边的最大位置
        int maxSumLeft=-10000;//记录左边的最大和
        int sumLeft=0;
        for (int i = mid; i >= low; i--)
         {
           sumLeft += a[i];
           if (sumLeft > maxSumLeft)
            {
              maxSumLeft = sumLeft;
              maxLeft = i;
            }
         }
        //求中点右边的最大值和最大位置
        int maxRight=mid+1;//记录右边的最大位置
        int maxSumRight = -10000;//记录右边的最大和
        int sumRight = 0;//记录右边子数列的和
        for (int i = mid+1; i <= high; i++)
          {
           sumRight += a[i];
           if (sumRight > maxSumRight)
            {
             maxSumRight = sumRight;
             maxRight = i;
           }
        }
        PositioASum ps;
        ps.low = maxLeft;
        ps.high = maxRight;
        ps.sum = maxSumLeft + maxSumRight;
           return ps;
     }
     //分治法
     PositioASum FindMaxSubArray(int a[], int low, int high)
     {
         if (low == high)
          {
            PositioASum ps;
            ps.low = low;
            ps.high = high;
            ps.sum = a[low];
             return ps;
          }
        else{
             int mid = (low + high) / 2;
             PositioASum left = FindMaxSubArray(a, low, mid);
             PositioASum right = FindMaxSubArray(a, mid + 1, high);
             PositioASum cross = MaxCrossingSubarray(a, low, mid, high);
             if (left.sum >= cross.sum && left.sum >= right.sum)
             {
                return left;
              }
             else if (right.sum >= left.sum && right.sum >= cross.sum)
                 {
                   return right;
                 }
                  else{
                       return cross;
                    }
             }
      }
     
     int _tmain(int argc, _TCHAR* argv[])
      {
          int A[8] = {-1,0,0,0,-1};
          PositioASum result = FindMaxSubArray(A, 0, 4);
          printf("%d  %d  %d", result.low, result.high, result.sum);//将结果打印出来
           getchar();
           return 0;
    }
    

    算法的时间复杂度为O(nlogn),由于本程序是在数组的原地址上面进行的,所以总体的控件复杂度为递归的时间复杂度+数组所占的空间为S(n)+S(logn)=S(n)

    3.动态规划
    令cursum(i)表示数组下标以i为起点的最大连续下标最大的和,而maxsum(i)表示前i个元素的最大子数组之和。那么我们就可以推出下一个maxsum(i+1)应该为cursum(i+1)和maxsum(i)中选取一个最大值。递推式为:
    cursum(i) = max{A[i],cursum(i-1)+A[i]};
    maxsum(i) = max{maxsum(i-1),cursum(i+1)};
    代码如下:

     //在线法求最大子数组和问题
    int _tmain(int argc, _TCHAR* argv[])
     {
       int A[8] = { -6, 10, -5, 6, -7, -1, -1 };
       int array_length = sizeof(A) / sizeof(A[0]);//数组大小
       int sum = 0;//记录子数组的和
       int thisSum = 0;
       int low=0;//记录子数组的底
       int height=0;//记录子数组的高
       for (int i = 0; i < array_length; i++)
       {
          thisSum += A[i];
          if (thisSum > sum)
           {
             sum = thisSum;
           }
          else if (thisSum < 0)
           {
            thisSum = 0;
           }
      }
       printf("%d",sum);//将结果打印出来
       getchar();
       return 0; 
    }
    

    这种算法时间复杂度只是O(n),效果非常好!

    二、代码链接

    有点意思

    三、运行结果

    四、流程图设计

    条件组合 执行路径
    array>0 ABI
    array<0,sum<=0,sum>maxSum ACEFGHI
    array<0,sum>0,sum>maxSum ADFGHI
    array<0,sum>0,sum<=maxSum ACDFHI
    array<0,sum<=0,sum<=maxsum ACEFHI

    实例(2,2,-4,2)的执行路径可覆盖:ACEFGHI、ADFGHI、ACDFHI、ACEFHI
    实例(-1,-2,-3,-4,-5)的执行路径可覆盖:ABI

    五、单元测试工具

    编译环境为Eclipse里自带工具JUnit4单元测试工具

    六、工作日志

    项目 记录结果
    日期 2020年4月3日
    开始时间 01:30
    结束时间 24:30
    编码行数 97
    错误数量 2
    错误1 当输入n=0时,计算结果不为0
    错误1修改时间 2 min
    错误2 JUnit4 中的参数化自动测试运行报错
    错误2修改时间 1.5 h

    七、体会

    这次作业是让我们熟悉“高质量”软件开发过程,虽让我们这次只是做一个程序的开发,但我也从中学到了很多。
    比如:针对你的程序的流程图如何去设计测试用例,使用例尽可能的覆盖到各个语句判断、分支等等,不止走的通的路能走,走不通的路也得设计能走通。
    还学会了Eclipse中的单元测试工具和参数化自动测试的方法。感觉自己还是欠缺许多,还得继续努力。

  • 相关阅读:
    时间随手记
    laravel简书(2)
    laravel简书(1)
    <<Design Patterns>> Gang of Four
    计算机网络你还懵逼吗?持续更新!!!
    poj3126 搜索
    POJ 1426 搜索进阶
    CodeForces 660D
    poj3279搜索详解
    bfs简单题-poj2251
  • 原文地址:https://www.cnblogs.com/muyouzhi/p/12630027.html
Copyright © 2020-2023  润新知