扫描算法求一个向量的元素和最大的连续子向量

来源:互联网 发布:论文图是用什么软件 编辑:程序博客网 时间:2024/05/17 09:17

来源于《编程珠玑》第八章的介绍:求一个向量的元素和最大的连续子向量,使用扫描算法可以将计算量化简到O(n)量级。

 

问:向量X  = { -2, 7, 1, -6, -2, 9, 2, -1 }(n=8) , 求它的和最大的连续子向量。

先看求解过程:

 

X                { -2, 7, 1, -6, -2, 9, 2, -1 }

max_end   {   0, 7, 8,  2, 0, 9, 11,10 } max_end = max( max_end + X(i), 0)

max_sofar {   0, 7, 8,  8, 8, 9, 11,11 } max_sofar = max(max_end, max_sofar);

 

解释一下:

       max_end 类似于积分值,但又有不同。

 

X                { -2, 7, 1, -6, -2, 9, 2, -1 }

max_end   {  0, 7, 8,  2,  0,  9,11,10 }

integration{ -2, 5, 6,  0, -2,  7, 9,  8} 

max_end 有较强的物理意义:max_end 期望尝试向右累加以获得最大子向量。

 

如果X全是正数,max_end 向右累加持续增大;

如果X全是负数,max_end 将一直为0;

如果X有正有负,假设max_end 现在为正,max_end 向右累加,当加到小于0时,有一点可以肯定X(i)<0, 发现没有必要再向右加了,把当前max_end 置0,继续扫描。

 

从左往右扫描,如果第一个数是负的,抛弃(max_end = 0);

                           第二个数是正的,开始累加,和max_sofar比较并将最大值记录在max_sofar里;

                           第三个数,累加后值还是正的,和max_sofar比较并将最大值记录在max_sofar里;

                           第四个数,累加后值还是正的,和max_sofar比较并将最大值记录在max_sofar里;

                           第五个数,累加后值是0,说明累加值已经递减到0,可得二、三、四、五属于一个山峰,其中二、三是局部最大子

                                          向量。此时max_sofar = 8,记录了局部山顶的值8,并且是在第三个数的时候到达。

                           第六个数是正的,开始新的累加,和max_sofar比较并将最大值记录在max_sofar里;

                           第七个数,累加后值还是正的,和max_sofar比较并将最大值记录在max_sofar里;

                           第八个数,累加后值还是正的,和max_sofar比较并将最大值记录在max_sofar里;此时max_sofar = 11,记录

                                          了局部山顶的值11,并且是在第七个数的时候到达。 

        

可见,这个方法扫描出一座座山峰(不能合并的局部最大和子向量),最后看看哪个子向量的海拔高(和最大)。max_end中的0指示了山峰的山脚(一座山的开始和结束)。这个算法最核心的地方是何时将max_end 置0。光是积分显然做不到,

max_end = max( max_end + X(i), 0)这句代码提供了何时以及怎么处理负数的核心问题。

源码实现如下:
 
#include <stdio.h>
#include <stdlib.h>
#define N 8
int max_sub_sum(const int *x, const int n);
int max(int a, int b);
int main()
{
 const int x[N] = { -2, 7, 1, -6, -2, 9, 2, -1 };
 
 int result = max_sub_sum(x, N);
 printf("Result: %d/n", result);
 return 0;
}
int max_sub_sum(const int *x, const int n)
{
 int max_sofar = 0;
 int max_ending = 0;
 for (int i = 0; i < n; ++i)
 {
  max_ending = max(max_ending + x[i], 0);
  max_sofar = max(max_sofar, max_ending);
 }
 return max_sofar;
}
int max(int a, int b)
{
 return (a > b) ? a : b;
}