求序列的最大子序列

看《编程珠玑》一书,讲解求序列的最大子序列。

问题: 给定一个实数序列x1,x2,…,xn(不必是正数),寻找一个连续的子序列xi,x(i+1),…,xj,使得其数值之和在所有连续子序列数值之和中是最大的。

 算法1

思路:比较所有连续序列数值的和,找到最大的。

1. maxsofar=0;

2. for i=[0,n)

3.   sum=0;

4.   for j=[i,n)

5.      sum=sum+x[j];  

6.      maxsofar=max(sum,maxsofar);

算法复杂度是O(n2)。

 

算法2

思路:在算法1的基础上改进。算法1中,第5行要重复求和。用一个数组预先存放从数组第一个元素开始到各个元素之间所有元素的和。比如定义数组 a[n+1],

a[0]=0,a[i]=x0+x1+…+x(i-1),(1=<i<=n)。

 

a[0]=0;

for i=[1..n]

   a[i]=a[i-1]+x[i-1];

maxsofar=0;

for i=[0,n)

sum=0;

for j=[i,n)

    sum=a[j+1]-a[i];

    maxsofar=max(sum,maxsofar).

 

算法复杂度是O(n2)。

 

算法3

思路:运用归纳法的思想。如果已知x[0…i-1]的最大子序列,那么能知道x[0…i]的最大子序列吗?所有不包含x[i]的子序列最大的就是x[0…i-1]的最大子序列,假设为S(i-1),因此,找到包含x[i]的子序列的最大子序列,然后用这个子序列和S(i-1)比较得到x[0..i]的最大子序列。

 

刚开始我写出来的算法:

a[0]=0;

for i=[1..n]

   a[i]=a[i-1]+x[i-1];

maxsofar=0;

for i=[0…n)

sum=0;

for j=[0..i-1)

    sum=max(sum,a[i+1]-a[j])

     maxsofar=max(maxsofar,sum);

算法复杂度O(n2)。

其算法复杂度跟上面的一样。似乎没有改进。问题出在计算包含xi的最大子序列。我开始简单看过程序,这个计算过程没有这么复杂。我在想是不是也用归纳法试试求这个最大子序列。

如果 x0,x1,…x(i-1)已经知道了包含x(i-1)的最大子序列S(i-1),那么如何能求x0,x1,…,xi,包含xi的最大子序列?包含xi的一个子序列=包含x(i-1)的子序列+xi,其中变化的是“包含x(i-1)的子序列”,不变的是xi,因此max(包含xi的一个子序列)=max(包含x(i-1)的子序列)+xi,当然如果max(包含x(i-1)的子序列)+xi < 0,则max(包含xi的一个子序列)=0,所以,

max(包含xi的一个子序列)=max(max(包含x(i-1)的子序列)+xi,0)。

这样程序就变更新为:

 

maxsofar=0;

maxtoend=0;

for i=[0…n)

maxtoend=max(maxtoend+x[i],0);

maxsofar=max(maxsofar,maxtoend);

算法复杂度是O(n)。

 

求最大子序列算法的历史:

布朗大学的UIf Grenander所面对的模式匹配问题中:最初的问题所描述的是二维数组。因为二维问题要求的时间太多以至于不能解决,所以Grenander将它简化为一维以对其结构有更好的了解。开始Grenander提出了O(n3)的算法(参加《编程珠玑》第8章),后来提出了O(n2)的算法。

他于1977年将该问题描述给Michael Shamos,这个家伙花一个通宵设计了分治的算法。不久以后,当Shamos向Bentley介绍这个问题时,他们一致认为那也许是可能的算法中最快的。

Shamos出息Carnegie Mellon研讨会,在该会议上向统计学家Jay Kadane描述了该问题以及相关历史,Jay Kadeane不出几分钟就给出了归纳算法的提纲。

 

现在我们看到归纳算法很简单,虽然Jay Kadane很快发现了这个问题,但是当时Ulf Grenander,Michael Shamos以及Bentley三个聪明的人也没有“显而易见”地发现这个算法。由此可见一个方法的发现并非是那么容易的。我们在解决问题时,同样也并非轻而易举的就能发现问题的解决方法。当我们没有解决一个问题时,完全不需要失望、沮丧,那是我们可能没有具备相关的知识、背景,灵活运用知识的能力不是突出等等客观原因所致。要看到我们必然能解决很多的其它问题,保持对自己的信任、保持学习的热情,这样自己的能力必然会有增强。


你可能感兴趣的:(编程,算法,n2)