看《编程珠玑》第8章,里面一章介绍了针对一个模式匹配问题算法的优化策略,由于优化后算法与原始算法效率上的大幅度增强,我被震惊了。
问题:
在一个向量中寻找最大子向量。
书上有伪代码,我还是实现了一下。
原始算法,一个3重循环
int alg1(int* x,int count) { int maxsofar = 0; for(int i=0;i<count;i++) { for (int j=i;j<count;j++) { int sum = 0; for (int k=i;k<j;k++) { sum +=x[k]; } maxsofar = max(maxsofar,sum); } } return maxsofar; }简单改进,最内重循环浪费了先前的加法结果,于是3阶循环可以优化到2阶:
int alg2(int* x,int count) { int maxsofar = 0; for(int i=0;i<count;i++) { int sum = 0; for (int j=i;j<count;j++) { sum += x[j]; maxsofar = max(maxsofar,sum); } } return maxsofar; }此算法同样是2阶,不过它先开辟内存空间计算了和,数组的[-1]位置的值先保存了一下,算完后还原,书上说不保存也没问题,我一试直接堆栈出错了。
int alg2b(int* x,int count) { int cumarr[MAXNUM]; int hold = cumarr[-1]; cumarr[-1] = 0; for(int i=0;i<count;i++) cumarr[i] = cumarr[i-1] + x[i]; int maxsofar = 0; int sum; for (int i=0;i<count;i++) { for (int j=i;j<count;j++) { sum = cumarr[j] - cumarr[i-1]; maxsofar = max(maxsofar, sum); } } cumarr[-1] = hold; return maxsofar; }接着优化。
接下来是分治思想的运用,分治无疑是将一个大问题分成一些小问题,这里首先将整个数组分成2个小数组,于是最大子数组要么在前一个中,要么在后一个中,要么就是跨越了这2个数组,在它们中间。如果是跨越的,那么这个最大子数组就围绕在前后数组分界数位置,可以从这个分界数开始向前获得最大子数组,向后获得最大子数组,其和便是跨越的最大子数组了。分治由于是2分,其效率为O(nlogn)
int alg3(int* x,int count,int l,int u) { int lmax,rmax,sum,m; if(l > u) return 0; if(l == u) return max(0,x[l]); m = (l+u)/2; lmax = sum = 0; for (int i=m;i>=l;i--) { sum += x[i]; lmax = max(lmax,sum); } rmax = sum = 0; for (int i=m+1;i<=u;i++) { sum += x[i]; rmax = max(rmax,sum); } return max(lmax+rmax, max(alg3(x,count,l,m), alg3(x,count,m+1,u))); }最后是一个NB之极的扫描算法,只有一阶!原理:前i个元素中,最大总和子数组要么在前i-1个元素中(我们将其存储在maxsofar中),要么其结束位置为i(我们将其存储在maxendinghere中)。代码很精辟,我觉得即使知道了原理也不一定写得出这样的代码。
int alg4(int* x, int count) { int maxsofar = 0; int maxendinghere = 0; for (int i=0;i<count;i++) { maxendinghere = max(maxendinghere+x[i],0); maxsofar = max(maxsofar,maxendinghere); } return maxsofar; }最后是结果:count = 10000,这个量级,算法1需要好几分钟,我都懒得让它算了。
这个让程序生成一个随机数组,并且由于种子固定,每次运行程序,这个随机数组是固定的。
int x[MAXNUM]; srand(1);//array is same when running the program everytime for(int i=0;i<MAXNUM;i++) x[i] = rand()%100-50;
alg2 result: 5264 | time cost: 2.839s
alg2b result: 5264 | time cost: 2.791s
alg3 result: 5264 | time cost: 0.011s
alg4 result: 5264 | time cost: 0.001s
那0.001也夸大了,实际在毫秒数量级之下。