1 #include  < fstream >
  2 #include  < iostream >
  3 #include  < time.h >
  4 #include  < sys / timeb.h >
  5 #include  < sys / time.h >
  6 #include  < vector >
  7
  8 using   namespace  std;
  9
 10 double  timeDelay()
 11 {
 12    double delay = 0;
 13    static timeval first;
 14    timeval second;
 15    gettimeofday(&second,0);
 16    delay = (second.tv_sec-first.tv_sec)*1000000+(second.tv_usec-first.tv_usec);
 17    first = second;
 18    return static_cast<int>(delay);
 19}

 20 /**/ /*
 21 *double timeDelay()
 22 *{
 23 *    double delay = 0;
 24 *    static timeb first;
 25 *    timeb second;
 26 *    ftime(&second);
 27 *    delay = (second.time - first.time)*1000 + (second.millitm-first.millitm);
 28 *    first = second;
 29 *    return static_cast<int>(delay);
 30 *}
 31 */

 32
 33 /**/ /*
 34 *int timeDelay()
 35 *{
 36 *    double delay = 0;
 37 *    static time_t first;
 38 *    time_t second;
 39 *    second = time(NULL);
 40 *    delay = second - first;
 41 *    first = second;
 42 *    return static_cast<int>(delay);
 43 *}
 44 */

 45
 46 // cubic maximum contiguous subsequence sum algorithm.
 47 int  maxSubSum1( const  vector < int >&  a)
 48 {
 49    int startIndex = 0;
 50    int endIndex = 0;
 51    int maxSum = 0;
 52    for ( int i=0; i<a.size(); ++i)
 53        for ( int j=i; j<a.size(); ++j)
 54        {
 55            int thisSum = 0;
 56            int k=i;
 57            for ( ; k<=j; k++{
 58                thisSum += a[k];
 59                if ( thisSum>maxSum) {
 60                    maxSum = thisSum;
 61                    startIndex = i;
 62                    endIndex = k;
 63                }

 64            }

 65        }

 66    cout << "in maxSubSum1\nstartIndex is " << startIndex << endl
 67        << "endIndex is " << endIndex << endl
 68        << "maxSum is " << maxSum << endl;
 69    return maxSum;
 70}

 71
 72 // Quadratic maximum contiguous subsequence sum algorithm
 73 int  maxSubSum2( const  vector < int >&  a)
 74 {
 75    int startIndex = 0;
 76    int endIndex = 0;
 77    int maxSum = 0;
 78    for ( int i=0; i<a.size(); ++i) {
 79        int thisSum = 0;
 80        for ( int j=i; j<a.size(); ++j) {
 81            thisSum += a[j];
 82            if ( thisSum>maxSum) {
 83                maxSum = thisSum;
 84                startIndex = i;
 85                endIndex = j;
 86            }

 87        }

 88    }

 89    cout << "in maxSubSum2\nstartIndex is " << startIndex << endl
 90        << "endIndex is " << endIndex << endl
 91        << "maxSum is " << maxSum << endl;
 92    return maxSum;
 93}

 94
 95 int  maxSumRec( const  vector < int >&  a, int  left,  int  right)
 96 {
 97    int startIndex = 0;
 98    int endIndex = 0;
 99
100    if (left==right) //Base case
101        if (0==a[left])
102            return a[left];
103        else
104            return 0;
105
106    int center = (left+right)/2;
107    int maxLeftSum = maxSumRec(a,left,center);
108    int maxRightSum = maxSumRec(a,center+1,right);
109
110
111    int maxLeftBorderSum = 0, leftBorderSum = 0;
112    for ( int i=center; i>=left; i--{
113        leftBorderSum += a[i];
114        if ( leftBorderSum>maxLeftBorderSum)
115            maxLeftBorderSum = leftBorderSum;
116    }

117
118    int maxRightBorderSum = 0, rightBorderSum = 0;
119    for ( int i=center+1; i<=right; i++{
120        rightBorderSum += a[i];
121        if ( rightBorderSum>maxRightBorderSum)
122            maxRightBorderSum = rightBorderSum;
123    }

124
125    int re;
126
127    if (maxLeftSum > maxRightSum) 
128        re = maxLeftSum;
129    else
130        re = maxRightSum;
131
132    if (re < (maxLeftBorderSum+maxRightBorderSum))
133        re = (maxLeftBorderSum+maxRightBorderSum);
134
135    return re;
136}

137
138 // Recursive maximum contiguous subsequence sum algorithm.
139 // Finds maximum sum in subarray spanning a[left..right].
140 // Does not attempt to maintain actual best sequence.
141 int  maxSubSum3( const  vector < int >&  a)
142 {
143    return maxSumRec(a,0,a.size()-1);
144}

145
146 // Linear-time maximum contiguous subsequence sum algorithm.
147 int  maxSubSum4( const  vector < int >&  a)
148 {
149    int thisSum = 0;
150    int maxSum = 0;
151    for ( int i=0; i<a.size(); ++i) {
152        thisSum += a[i];
153        if (thisSum<=0{
154            thisSum = 0;
155        }
 else if (thisSum>maxSum) {
156            maxSum = thisSum;
157        }

158    }

159    cout << "in maxSubSum4, maxSum is " << maxSum << endl;
160    /**//*
161     *cout << "in maxSubSum1\nstartIndex is " << startIndex << endl
162     *    << "endIndex is " << endIndex << endl
163     *    << "maxSum is " << maxSum << endl;
164     */

165    return maxSum;
166}

167
168 int  main()
169 {
170    vector<int> array;
171    fstream input;
172    input.open("data.txt");
173    if (!input) {
174        cout << "file open failure" << endl;
175    }

176
177    int temp;
178    while (input >> temp) {
179        array.insert(array.end(),temp);
180    }

181    input.close();
182
183    timeDelay();
184
185    cout << "=====================================\n";
186    maxSubSum1(array);
187    cout << timeDelay() << endl;
188
189    cout << "=====================================\n";
190    maxSubSum2(array);
191    cout << timeDelay() << endl;
192
193    cout << "=====================================\n";
194    cout << "maxSum is " << maxSubSum3(array) << endl;
195    cout << timeDelay() << endl;
196
197    cout << "=====================================\n";
198    maxSubSum4(array);
199    cout << timeDelay() << endl;
200    return 0;
201
202}

203



该笔记是学习《数据结构与算法分析》P35-43的记录与心得

第三种方法没有搞明白怎么去计算子序列开始和结束的索引,懂的同学看到了告诉我,谢谢~~

最大的子序列和问题:
给定整数A1,A2,。。。,AN(可能有负数),求sum(Ai+A(i+1)+...A(j))的最大值
例如,对于输入-2,11,-4,13,-5,-2,答案为20(从A2到A4)

算法一是穷举式的尝试所有的可能。运行时间为O(N的3次方)
算法二通过撤出一个for循环来避免三次运行时间,通过式子
sum(a(i)+a(i+1)+...+a(j))=a(j)+sum(a(i)+a(i+1)+...+a(j-1))
因此我们可以去掉最内一层循环,O(N的2次方)
算法三采用分治策略(divide and conquer)策略。其想法是把问题分成两个大致相等的自问题,然后递归的对他们求解,这是“分”的部分。“治”阶段将两个子问题的解合并到一起并可能再作少量的附加工作,最后得到整个问题的解。
在我们的例子中,最大子序列和可能出现在三处地方,或者整个出现在输入数据的左半部,或者整个出现在右半部,或者跨越输入数据的中部从而占据左右两半部分。前两种情况可以递归求解。第三种情况的最大和可以通过求出前半部分的最大和(必须包含前半部分的最后一个元素)以及后半部分的最大和(包含后半部分的第一个元素)而得到。然后将两个和加在一起。例如对于序列:
4 -3 5 -2 -1 2 6 -2
前半部分最大子序列和为6,后半部分为8,跨越两部分通过中间的为4+7=11,因此对该序列最好的情况为第三种情况。
对于程序,100-104行处理基准情况
107-108行执行两个递归调用
111-116,118-123行计算达到中间分界处的两个最大的和数,因为必须要从某个元素开始来求解最大子序列,所以可以这么写for循环。
算法三比前面两种工作量的都要大,但是程序短并不是总意味着程序好。
算法四是一个聪明的算法:
如果我们不需要知道最佳的子序列在哪里,那么i的使用可以从程序中优化掉。
1.如果下一个数是负数,那么我们就记录下当前的最大和。
  如果加上该负数后,我们的和还是正数,那么我们就不改变最大和,但记录下此时的和,继续往下加,或许出现值使得和重新超过最大和。
  如果加上该负数后,我们的和是负数了。那么我们就不该遍最大和,此时的和清零,继续往下加,开始一个新的子序列,或许出现值使得和重新超过最大和。
2.如果下一个数是整数,那么就往下加,更新此时的和以及最大和。

该算法一个附带的优点是:
它只对数据进行一次扫描,一旦a[i]被读入并被处理,它就不需要被记忆。因此,如果数组在磁盘或者磁带上,它就可以被顺序读入,在主存中不必存储数组的任何部分。不仅如此,在任意时刻,算法都能对它已经读入的数据给出子序列问题的正确答案(其他算法不具有这个特性)。具有这种特性的算法叫做联机算法(on-line algorithm).仅需常量空间并以线性时间运行的联机算法几乎是完美的算法。


该程序输出结果如下:
第一次:
=====================================
in maxSubSum1
startIndex is 226
endIndex is 650
maxSum is 2276
8.89795e+06
=====================================
in maxSubSum2
startIndex is 226
endIndex is 650
maxSum is 2276
17265
=====================================
maxSum is 2276
277
=====================================
in maxSubSum4, maxSum is 2276
27

第二次:
=====================================
in maxSubSum1
startIndex is 226
endIndex is 650
maxSum is 2276
8.86029e+06
=====================================
in maxSubSum2
startIndex is 226
endIndex is 650
maxSum is 2276
17781
=====================================
maxSum is 2276
278
=====================================
in maxSubSum4, maxSum is 2276
27


ps:
1,生成data文件的代码如下:我使用的大小为2000.

#include <iostream>
#include <fstream>
#include <stdlib.h>
using namespace std;

const int maxsize = 2000;
int main()
{
 fstream output;
 output.open("data.txt",ofstream::out);
 if (!output) {
  cout << "file open failed!" << endl;
  return 1;
 }
 int randint;

 for ( int i=0; i<maxsize; i++) {
  randint = rand()%100;
  if (0==rand()%2) {
   randint = 0-randint;
  }
  cout << randint << endl;
  output << randint << endl;
 }

 return 0;
}


2. timeDelay函数用于计算算法的运行时间,刚开始用的time,秒级别的。
发现记录时间时出现0,于是使用ftime,毫秒级别的,结果有几个算法时间相同,于是使用gettimeofday,微妙级别的,就可以看到程序运行时间的差别了~~
关于这几个函数的差别可以man一下,注意time要用man 2 time