一、算法設計
1、問題描述:輸入具有n個浮點數的向量x,輸出是輸入向量的任何連續子向量中的最大和。
有效解決方法:
(1)分治算法:要解決規模爲n的問題,可遞歸得解決兩個規模近似爲n/2的子問題,然後對他們的答案進行合併以得到整個問題的答案。
代碼如下,最大子向量要麼整個在a中,要麼整個在b中,要麼跨越a和b之間的邊界。跨越a和b 的邊界時,其最大子向量在a中的部分包含右邊界的最大子向量,在b中的部分包含左邊界的最大子向量。算法複雜度爲O(nlogn)。
float maxsum3(l, u)
if (l > u) return 0
if (l == u ) return max(0, x[l])
m = (l + u) / 2
lmax = sum = 0
for (i = m; i >= l; i--)
sum += x[i]
lmax = max (lmax, sum)
rmax = sum = 0
for i = (m, u]
sum += x[i]
rmax = max (rmax, sum)
return max(lmax+rmax, maxsum(l, m), maxsum3(m+1, u))
(2)一個O(n)複雜度的算法,從最左端(元素 x[0])開始,一直掃描到最右端(元素 x[n-1]),記下所碰到過的最大總和子數組。最大值初始爲0.假設已經解決了針對 x[0..i-1] 的問題,現在需要拓展到 x[i] 中。可以使用類似分治法中的道理,前 i 個元素中,最大總和子數組要麼在 i-1 個元素中(存儲在 maxsofar 中),要麼截止到位置 i(存儲在 maxendinghere中)。算法代碼更加簡短,但是運行起來是最快的,運行時間是O(n),已經是線性算法。源代碼如下:
float max_subvector5(float array[], int length)
{
int i;
float maxsofar = 0, maxendinghere = 0;
for(i = 0; i < length; i ++)
{
maxendinghere = (maxendinghere + x[i]) > 0 ? maxendinghere : 0;
maxsofar = maxsofar > maxendinghere ? maxsofar : maxendinghere;
}
return maxsofar;
}
二、總結
本章故事中的這些算法給出了幾個重要的算法設計技術:
1、保存狀態,避免重複計算。通過使用一些空間來保存中間計算結果,我們避免了花時間來對其重複計算。
2、將信息預處理到數據結構中。
3、分治算法。
4、掃描算法。與數組相關的問題經常可以通過思考“如何將x[0...i-1]的解擴展爲x[0...i]地解來解決。
5、累積。
6、下界。確定相匹配的下界。
三、習題
1、習題10:假設我們要查找的是總和最接近0的子向量,而不是具有最大總和的子向量,則如何設計算法?若查找總和最接近某一個給定實數t的子向量呢?
解答:初始化累積數組cum,使得cum[i] = x[0] + ... + x[i]。如果cum[l-1] = cum[u],那麼子向量x[l...u]之和就爲0.所以可以通過定位cum中最接近的兩個元素來找出和最接近零的子向量,可通過排序累積數組並比較相鄰元素間的大小從而得到結果。找最接近某一特定值的算法類似這樣。
2、習題13:在最大子數組問題中,給定m*n的實數數組,我們需要求出矩形子數組的最大總和。該問題的複雜度如何?
解答:可以在長度爲m的維度上使用平方複雜度的算法,而在長度爲n的維度上使用線性算法。可以在O(m^2*n)的時間複雜度內解決m*n問題。代碼如下所示:
float max_subarr(float **array, int row, int col)
{
int i, j, k;
float *vector, max = 0, maxsofar, maxendinghere;
assert(row > 0 && col > 0);
vector = (float *) malloc(col * sizeof(float));
assert(vector != NULL);
printf("the array :\n");
for( i = 0; i < row; i ++)
{
memset(vector, 0, col * sizeof(float));//初始化爲0
for( j = i; j < row; j ++)
{
//vector[k]的值爲行i至行j的第k列元素之和,把二維問題轉化爲一維問題。
for(k = 0; k < col; k ++)
vector[k] += array[j][k];//vector[k] = array[i][k] + array[i + 1][k] + ... + array[j][k],
maxsofar = vector[0];
maxendinghere = 0;
for(k = 0; k < col; k ++)
{
maxendinghere = (maxendinghere + vector[k]) > 0 ? (maxendinghere + vector[k]) : 0;
maxsofar = maxsofar > maxendinghere ? maxsofar : maxendinghere;
}
max = max > maxsofar ? max : maxsofar;
}
}
return max;
}