求一个给定序列的连续子序列中和最大的那个子序列的和,下边方法只求和,没有找出最大子序列。
#include "stdafx.h"
#include<vector>
#include <string>
#include <fstream>
#include <random>
#include <time.h>
#include <iostream>
typedef long int l_int;
该方法设计三层for训话,第一层设定求和序列的开始位置,第二层设定求和位置的结束位置,第三层对子序列进行求和。例如该序列为a1 a2 a3 a4 a5 a6,那么先a1为开始位置,分别以a2、a3、a4、a5、a6为结束位置,对五个子序列分别求和,找出其最大和sum。接着以a2为起始位置,a3、a4、a5、a6为结束位置,分别求出四个子序列的和,并且和sum比较,找出最大和。以此类推。
l_int MaxSubSum2(std::vector<int> data){
time_t t1,t2;
time(&t1);
l_int sum = 0;
for (int i=0;i<data.size();i++)
{
for (int j=i;j<data.size();j++)
{
l_int temp = 0;
for (int k=i;k<j;k++)
{
temp += data[k];
}
if (temp > sum)
{
sum = temp;
}
}
}
time(&t2);
std::cout<<"MaxSubSum2: "<<sum<<" time:"<<t2-t1<<std::endl;
return sum;
}
观察三层for穷举法,会发现在计算起始点相同的各个子序列和时,长序列包括了各个短序列的和。例如计算a1+a2+a3时,需要计算a1+a2。计算a1+a2+a3+a4+a5+a6时,可以分别得出各个子序列a1+a2、a1+a2+a3、a1+a2+a3+a4、a1+a2+a3+a4+a5的和,那么三层for穷举法可以做一下修改:
l_int MaxSubSum3(std::vector<int> data){
time_t t1,t2;
time(&t1);
l_int sum = 0;
for (int i=0;i<data.size();i++)
{
l_int temp = 0;
for (int j=i;j<data.size();j++)
{
temp += data[j];
if (temp > sum)
{
sum = temp;
}
}
}
time(&t2);
std::cout<<"MaxSubSum3: "<<sum<<" time:"<<t2-t1<<std::endl;
return sum;
}
如果a[i]是负数那么它不可能代表最有序列的起点,因为任何包含a[i]的作为起点的子序列都可以通过用a[i+1]作为起点来改进。类似的有,任何的负的子序列不可能是最优子序列的前缀。例如说,循环中我们检测到从a[i]到a[j]的子序列是负数,那么我们就可以推进i。关键的结论是我们不仅可以把i推进到i+1,而且我们实际可以把它一直推进到j+1。
l_int MaxSubSum4(std::vector<int> data){
time_t t1,t2;
time(&t1);
l_int sum = 0;
l_int temp = 0;
for (int i=0;i<data.size();i++)
{
temp += data[i];
if (temp > sum)
{
sum = temp;
}
else if(temp<0){
temp = 0;
}
}
time(&t2);
std::cout<<"MaxSubSum4: "<<sum<<" time:"<<t2-t1<<std::endl;
return sum;
}
最大子序列可能在三个地方出现,或者在左半部,或者在右半部,或者跨越输入数据的中部而占据左右两部分。前两种情况递归求解,第三种情况的最大和可以通过求出前半部分最大和(包含前半部分最后一个元素)以及后半部分最大和(包含后半部分的第一个元素)相加而得到。(算法导论)
l_int max3(l_int left,l_int right, l_int cross );
l_int re_MaxSubSum1(std::vector<int> data , int left, int right);
l_int MaxSubSum1(std::vector<int> data){
time_t t1,t2;
time(&t1);
l_int sum = re_MaxSubSum1(data,0,data.size()-1);
time(&t2);
std::cout<<"MaxSubSum1: "<<sum<<" time:"<<t2-t1<<std::endl;
return sum;
}
l_int re_MaxSubSum1(std::vector<int> data , int left, int right){
if (left == right)
{
//return (data[left]>0?data[left]:0);
return data[left];
}
int cent = (left+right)/2;
l_int MaxLeft = re_MaxSubSum1(data, left,cent);
l_int MaxRight = re_MaxSubSum1(data,cent+1,right);
l_int CrossSum_Ltemp = 0;
l_int CrossSumL = 0;
for (int i = cent;i>=left;i--)
{
CrossSum_Ltemp += data[i];
if (CrossSum_Ltemp>CrossSumL)
{
CrossSumL = CrossSum_Ltemp;
}
}
l_int CrossSum_Rtemp = 0;
l_int CrossSumR = 0;
for (int i = cent+1;i<=right;i++)
{
CrossSum_Rtemp += data[i];
if (CrossSum_Rtemp>CrossSumR)
{
CrossSumR = CrossSum_Rtemp;
}
}
l_int SumCross = CrossSumL+CrossSumR;
return max3(MaxLeft,MaxRight,SumCross);
}
l_int max3(l_int left,l_int right, l_int cross ){
l_int temp = left;
if (temp < right)
{
temp = right;
}
if (temp < cross)
{
temp = cross;
}
return temp;
}
http://blog.163.com/kevinlee_2010/blog/static/169820820201010495438247/作者对四种方法也做了总结,本文主要参考该作者的方法。
将测试文件的数据读入容器
bool readfile(std::vector<int> &data,std::string filename){
std::ifstream infile(filename);
if (!infile)
{
return false;
}
int s;
while (infile>>s)
{
data.push_back(s);
}
return true;
}
用C++11标准的随机数引擎和分布生成给定数量,一定范围内的随机数
bool writefile(l_int num,int max_nun,std::string filename = "text.txt"){
std::ofstream outfile(filename);
if (!outfile)
{
return false;
}
std::uniform_int_distribution<int> u(0,max_nun);
std::default_random_engine e;
for (int i =0;i<num;i++)
{
outfile<<(u(e)%2==0?u(e):~u(e))<<'\n';
}
return true;
}
分别调用四种方法,观察运行时间,实验证明,一层for循环穷举法换时间最少,三层for循环最多,但数据数目上千后递归法耗时不比二层for循环穷举法短,主要因为其间函数调用耗时长。
int _tmain(int argc, _TCHAR* argv[])
{
if(!writefile(100000,200)){
return 0;
}
std::vector<int> data;
if(!readfile(data,"text.txt")){
return 0;
}
l_int sum4 = MaxSubSum4(data);
l_int sum3 = MaxSubSum3(data);
l_int sum1 = MaxSubSum1(data);
l_int sum2 = MaxSubSum2(data);
return 0;
}