您的位置:首页 > 其它

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

2015-07-26 15:02 465 查看
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、数量级及其时间比较表格

Algorithm1234
TimeO(N^3)T/N RateO(N^2)T/N RateO(NlogN)T/N RateO(N)T/N Rate
Input Size(N)100.9030.09030.7710.07710.7340.07340.7070.0707
1001.0690.010690.7910.007910.7840.007840.7440.00744
10003.5380.0035380.9520.0009520.8050.0008050.780.00078
1000030000.31.5990.00015991.0670.00010670.8590.0000859
10000030000003073.8440.000738441.2550.000012551.1250.00001125
6、源代码及其测试数据

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <ctime>
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: