最大子序列和的四种求解算法及其时间比较

1、T(N)=O(N^3)

__int64 algorithm1(__int64 a[],__int64 n)    //T(N)=O(N^3)
{
    __int64 MaxSum = 0;
    __int64 ThisSum;
    for(__int64 i = 0; i < n; i++)  //i从零开始到最后一个数字
    {
        for(__int64 j = i; j < n; j++)  //j从i开始到最后一个数字
        {
            ThisSum = 0;
            //PS:第一轮循环k从0->0,0->1,0->2...0->n
            //PS:第二轮循环k从1->1,1->2,1->3...1->n
            //PS:第K轮循环k从k->k,k->k+1,k->k+2...k->n
            //PS:这样就遍历完序列中所有子序列的可能性
            for(__int64 k = i; k <= j; k++) //从i开始到j的数字相加
                ThisSum += a[k];
            if(ThisSum > MaxSum)    //大于最大子序列之和则替换
                MaxSum = ThisSum;
        }
    }
    return MaxSum;
}

2、T(N)=O(N^2)

__int64 algorithm2(__int64 a[],__int64 n)   //T(N)=O(N^2)
{
    __int64 MaxSum = 0;
    __int64 ThisSum;
    for(__int64 i = 0; i < n; i++)  //i从零开始到最后一个数字
    {
        ThisSum = 0;
        //PS:第一轮j从0->n,每循环一次,判断一次
        //PS:第二轮j从1->n,每循环一次,判断一次
        //PS:第J轮j从j->n,每循环一次,判断一次
        //PS:这样就遍历完序列中所有子序列的可能性
        for(__int64 j = i; j < n; j++)  //j从i开始到最后一个数字
        {
            ThisSum += a[j];
            if(ThisSum > MaxSum)    //大于最大子序列之和则替换
                MaxSum = ThisSum;
        }
    }
    return MaxSum;
}

3、T(N)=O(NlogN),底数为2

PS:用分治法,先将原序列分成相似的两个子序列,这样可以得出一个结论,
那就是最大子序列和要不在左子序列中,要不在右子序列中,要不在左子序列和右子序列的连接处
对左子序列递归求最大序列和,对右子序列求最大序列和,再将左右子序列连接起来,求连接处的最大序列和
最后比较就能求出整体最大序列和

__int64 MaxSubSum(__int64 a[],__int64 Left,__int64 Right)
{
    __int64 Center; //中间元素
    __int64 MaxLeftSum,MaxRightSum; //左右子序列最大序列和
    __int64 MaxLeftBorderSum,MaxRightBorderSum; //左右边界最大序列和
    __int64 LeftBorderSum,RightBorderSum;   //左右边界序列和
    if(Left == Right)   //递归出口
        if(a[Left] > 0) //当a[i]>0时,返回该元素
            return a[Left];
        else            //否则返回0
            return 0;

    Center  = (Left + Right) / 2;
    MaxLeftSum = MaxSubSum(a,Left,Center);  //递归求解左子序列
    MaxRightSum = MaxSubSum(a,Center + 1,Right); //递归求解右子序列

    MaxLeftBorderSum = 0;
    LeftBorderSum = 0;
    for(__int64 i = Center; i>= Left; i--)  //求左边界最大序列和
    {
        LeftBorderSum += a[i];
        if(LeftBorderSum > MaxLeftBorderSum)
            MaxLeftBorderSum = LeftBorderSum;
    }
    MaxRightBorderSum = 0;
    RightBorderSum = 0;
    for(__int64 i = Center + 1; i <= Right; i++) //求右边界最大序列和
    {
        RightBorderSum += a[i];
        if(RightBorderSum > MaxRightBorderSum)
            MaxRightBorderSum = RightBorderSum;
    }
    return max(max(MaxLeftSum,MaxRightSum),MaxLeftBorderSum+MaxRightBorderSum); //返回整体最大序列和
}

__int64 algorithm3(__int64 a[],__int64 n)   //T(N)=O(NlogN),底数为2
{
    return MaxSubSum(a,0,n - 1);
}

4、T(N)=O(N)

推荐用这种算法

__int64 algorithm4(__int64 a[],__int64 n)     //T(N)=O(N)
{
    __int64 ThisSum = 0;
    __int64 MaxSum = 0;
    for(int i = 0; i < n; i++)  //i从零开始到最后一个数字
    {
        ThisSum += a[i];
        if(ThisSum < 0) //当a[j]+...+a[i] < 0,则舍弃i之前的所有序列,从i后继续累加
            ThisSum = 0;
        if(ThisSum > MaxSum) //求出当前序列的最大子序列和
            MaxSum = ThisSum;
    }
    return MaxSum;
}

5、数量级及其时间比较表格

Algorithm 1 2 3 4
Time O(N^3) T/N Rate O(N^2) T/N Rate O(NlogN) T/N Rate O(N) T/N Rate
Input Size(N) 10 0.903 0.0903 0.771 0.0771 0.734 0.0734 0.707 0.0707
100 1.069 0.01069 0.791 0.00791 0.784 0.00784 0.744 0.00744
1000 3.538 0.003538 0.952 0.000952 0.805 0.000805 0.78 0.00078
10000 3000 0.3 1.599 0.0001599 1.067 0.0001067 0.859 0.0000859
100000 3000000 30 73.844 0.00073844 1.255 0.00001255 1.125 0.00001125

6、源代码及其测试数据

#include 
#include 
#include 
#include 
using namespace std;

__int64 algorithm1(__int64 a[],__int64 n)    //T(N)=O(N^3)
{
    __int64 MaxSum = 0;
    __int64 ThisSum;
    for(__int64 i = 0; i < n; i++)  //i从零开始到最后一个数字
    {
        for(__int64 j = i; j < n; j++)  //j从i开始到最后一个数字
        {
            ThisSum = 0;
            //PS:第一轮循环k从0->0,0->1,0->2...0->n
            //PS:第二轮循环k从1->1,1->2,1->3...1->n
            //PS:第K轮循环k从k->k,k->k+1,k->k+2...k->n
            //PS:这样就遍历完序列中所有子序列的可能性
            for(__int64 k = i; k <= j; k++) //从i开始到j的数字相加
                ThisSum += a[k];
            if(ThisSum > MaxSum)    //大于最大子序列之和则替换
                MaxSum = ThisSum;
        }
    }
    return MaxSum;
}

__int64 algorithm2(__int64 a[],__int64 n)   //T(N)=O(N^2)
{
    __int64 MaxSum = 0;
    __int64 ThisSum;
    for(__int64 i = 0; i < n; i++)  //i从零开始到最后一个数字
    {
        ThisSum = 0;
        //PS:第一轮j从0->n,每循环一次,判断一次
        //PS:第二轮j从1->n,每循环一次,判断一次
        //PS:第J轮j从j->n,每循环一次,判断一次
        //PS:这样就遍历完序列中所有子序列的可能性
        for(__int64 j = i; j < n; j++)  //j从i开始到最后一个数字
        {
            ThisSum += a[j];
            if(ThisSum > MaxSum)    //大于最大子序列之和则替换
                MaxSum = ThisSum;
        }
    }
    return MaxSum;
}

//PS:用分治法,先将原序列分成相似的两个子序列,这样可以得出一个结论,
//那就是最大子序列和要不在左子序列中,要不在右子序列中,要不在左子序列和右子序列的连接处
//对左子序列递归求最大序列和,对右子序列求最大序列和,再将左右子序列连接起来,求连接处的最大序列和
//最后比较就能求出整体最大序列和
__int64 MaxSubSum(__int64 a[],__int64 Left,__int64 Right)
{
    __int64 Center; //中间元素
    __int64 MaxLeftSum,MaxRightSum; //左右子序列最大序列和
    __int64 MaxLeftBorderSum,MaxRightBorderSum; //左右边界最大序列和
    __int64 LeftBorderSum,RightBorderSum;   //左右边界序列和
    if(Left == Right)   //递归出口
        if(a[Left] > 0) //当a[i]>0时,返回该元素
            return a[Left];
        else            //否则返回0
            return 0;

    Center  = (Left + Right) / 2;
    MaxLeftSum = MaxSubSum(a,Left,Center);  //递归求解左子序列
    MaxRightSum = MaxSubSum(a,Center + 1,Right); //递归求解右子序列

    MaxLeftBorderSum = 0;
    LeftBorderSum = 0;
    for(__int64 i = Center; i>= Left; i--)  //求左边界最大序列和
    {
        LeftBorderSum += a[i];
        if(LeftBorderSum > MaxLeftBorderSum)
            MaxLeftBorderSum = LeftBorderSum;
    }
    MaxRightBorderSum = 0;
    RightBorderSum = 0;
    for(__int64 i = Center + 1; i <= Right; i++) //求右边界最大序列和
    {
        RightBorderSum += a[i];
        if(RightBorderSum > MaxRightBorderSum)
            MaxRightBorderSum = RightBorderSum;
    }
    return max(max(MaxLeftSum,MaxRightSum),MaxLeftBorderSum+MaxRightBorderSum); //返回整体最大序列和
}

__int64 algorithm3(__int64 a[],__int64 n)   //T(N)=O(NlogN),底数为2
{
    return MaxSubSum(a,0,n - 1);
}

__int64 algorithm4(__int64 a[],__int64 n)     //T(N)=O(N)
{
    __int64 ThisSum = 0;
    __int64 MaxSum = 0;
    for(int i = 0; i < n; i++)  //i从零开始到最后一个数字
    {
        ThisSum += a[i];
        if(ThisSum < 0) //当a[j]+...+a[i] < 0,则舍弃i之前的所有序列,从i后继续累加
            ThisSum = 0;
        if(ThisSum > MaxSum) //求出当前序列的最大子序列和
            MaxSum = ThisSum;
    }
    return MaxSum;
}

__int64 Random(__int64 m,__int64 n)    //指定范围内随机数
{
    __int64 pos,dis;
    if(m == n)  //m=n则表示范围内只有一个数字
    {
        return m;
    }
    else if(m > n)  //m>n则说明取[m,n]区间内数字
    {
        pos = n;
        dis = m - n + 1;
        return rand() % dis + pos;
    }
    else    //m>n则说明取[n,m]区间内数字
    {
        pos = m;
        dis = n - m + 1;
        return rand() % dis + pos;
    }
}

void MakeRandomSequences(__int64 m,__int64 n,__int64 num)   //产生随机序列
{
    ofstream filerandom("G:\\sequences.txt");
    srand((int)time(NULL)); //根据时间产生相应种子值
    for(int i=0 ; i < num; i++)
    {
        filerandom << Random(m,n) <<" ";
        if(!((i + 1) % 10)) //数据量逢十换行
            filerandom << endl;
    }
    filerandom.close();
}
int main()
{
//测试数据
//    __int64 a[] = {4,-3,5,-2,-1,2,6,-2};
//    __int64 n = 8;
//    cout << algorithm1(a,n) << endl;
//    cout << algorithm2(a,n) << endl;
//    cout << algorithm3(a,n) << endl;
//    cout << algorithm4(a,n) << endl;

//    MakeRandomSequences(-1000,1000,100000);  //产生随机序列

    __int64 a[200000];
    __int64 n = 0;
    ifstream filesequ("G:\\sequences.txt");
    if(!filesequ.is_open())   //打开失败处理
    {
        cout<<"Error opening file";
        return -1;
    }
    while(!filesequ.eof())
    {
        filesequ >> a[n++];
    }
    filesequ.close();

//PS:测试只需要把调用算法注释去掉
//    cout << algorithm1(a,n) << endl;
//    cout << algorithm2(a,n) << endl;
//    cout << algorithm3(a,n) << endl;
    cout << algorithm4(a,n) << endl;

    return 0;
}

你可能感兴趣的:(Algorithm)