求解最大子段和问题

求解最大子段和问题。

对于给定序列a1,a2,a3……an,寻找它的某个连续子段,使得其和最大。如( -2,11,-4,13,-5,-2 )最大子段是{ 11,-4,13 }其和为20。

要求:分别用教材所给的三种方法求解(简单方法、分治法、动态规划),通过实例比较结果和时间效率。

提交内容:算法的思想、程序源代码及其说明、实例运行结果及分

简单方法求解:

以a[0]开始: {a[0]}, {a[0],a[1]},{a[0],a[1],a[2]}……{a[0],a[1],……a[n]}共n个

以a[1]开始: {a[1]}, {a[1],a[2]},{a[1],a[2],a[3]}……{a[1],a[2],……a[n]}共n-1个

……

以a[n]开始:{a[n]}共1个

暴力破解方法简单直观,通过两层嵌套循环遍历所有可能的子段,计算它们的和,并保留最大和。这种方法的时间复杂度为O(n^2),适用于小规模数据集。

分治方法:

将序列a[1:n]分成长度相等的两段a[1:n/2]和a[n/2+1:n],分别求出这两段的最大字段和,则a[1:n]的最大子段和有三中情形:

a[1:n]的最大子段和与a[1:n/2]的最大子段和相同;

a[1:n]的最大子段和与a[n/2+1:n]的最大子段和相同;

a[1:n]的最大字段和为∑ak,且1<=i<=n/2,n/2+1<=j<=n。

可用递归方法求得情形1、2。对于情形3,可以看出a[n/2]与a[n/2+1]在最优子序列中。因此可以在a[1:n/2]中计算出

,并在a[n/2+1:n]中计算出 。则s1+s2即为出现情形3时的最优值。

        

        分治法采用递归的方式将问题划分成更小的子问题,分别求解子问题的最大子段和,然后合并得到整体的最大子段和。这种方法的时间复杂度为O(n log n),适用于中等规模的数据集。它的优势在于对于更大规模的问题,其效率相对较高。

动态规化:

D[i]表示从i开始的最大字段和。(但我们不是从前往后找字段结束位置)

根据递推公式,我们可知要想求得D[i],就必须知道D[i+1],所以我们从前往后计算。

如下图:以i=12开始的子段和D[12]=X[12]=-1,该子段结束位置Rec[12]=i=12;

当i=11时,D[11+1]<0,所以D[11]=X[11]=7,Rec[i]=i=11;

当i=10时,D[10+1]>0,所以D[10]=X[10]+D[11]=3+7,Rec=i+1=11;


一直到i,我们就找完了所有子段和,接着在子段和中找最大的。

        动态规划采用自底向上的方式,通过迭代计算子问题的最优解,并将结果保存起来,避免了重复计算。这种方法的时间复杂度为O(n),适用于大规模数据集。它的优势在于具有更好的时间效率,但相对于分治法,实现稍显复杂。

实验分析

暴力破解

源码:

//暴力破解

int sum1(int arr[],int len) {

    int sum=0;

    for (int i = 0; i < len; i++) {

         int this_sum = 0;

         for (int j = i; j < len; j++) {

             this_sum += arr[j];

             if (this_sum > sum) {

                  sum = this_sum;

             }

         }

    }

    return sum;

}

分治法:

//分治法

int maxSubSum(int a[], int left, int right) {



    int sum = 0;

    if (left == right) {

         sum = a[left] > 0 ? a[left] : 0;

    }

    else {



         int center = (left + right) / 2;

         int leftSum = maxSubSum(a, left, center);

         int rightSum = maxSubSum(a, center + 1, right);



         int s1 = 0;

         int lefts = 0;

         for (int i = center; i >= left; i--) {



             lefts += a[i];

             if (lefts > s1) s1 = lefts;

         }



         int s2 = 0;

         int rights = 0;

         for (int i = center + 1; i < right; i++) {



             rights += a[i];

             if (rights > s2)  s2 = rights;

         }

         sum = s1 + s2;

         if (sum < leftSum) sum = leftSum;

         if (sum < rightSum) sum = rightSum;

    }

    return sum;

}



int maxSum(int a[], int n) {

    return maxSubSum(a, 0, n - 1);

}

动态规划:

//动态规划

int sum2(int arr[],int len)

{

    int sum=0;

    int b=0;

    int left=0;

    int right=0;

    for (int i = 0; i < len; i++) {

         if (b > 0) {

             b += arr[i];

             right = i;

         }

         else {

             b = arr[i];



         }



         if (sum < b) {

             sum = b;



         }

    }

   

    return sum;



}



实验分析:

求解最大子段和问题_第1张图片求解最大子段和问题_第2张图片

测试了2000个数据,除了暴力破解比较慢用了8ms,其他的都不足1ms。

你可能感兴趣的:(算法分析与设计,算法)