第四章 分治策略

本章介紹分治法故源,首先通過前兩節(jié)最大子數(shù)組、矩陣乘法兩個問題說明分治法的一般步驟:分解汞贸,解決绳军,合并。當(dāng)子問題需要遞歸求解時稱為遞歸情況矢腻,足夠小可直接得出解為基本情況门驾,其余一些與原問題不同子問題看做合并步驟部分。
然后介紹三種遞歸式的解法:代入法多柑、遞歸樹法和主方法猎唁。

  1. 代入法:猜測算法的復(fù)雜度界,用數(shù)學(xué)歸納法證明正確性顷蟆。
  2. 遞歸樹法:將遞歸式轉(zhuǎn)換為樹,結(jié)點(diǎn)表示不同層次的遞歸調(diào)用產(chǎn)生的代價腐魂。然后采用邊界和技術(shù)求解帐偎。
  3. 主方法:求解如下遞歸式的界

一般忽略遞歸式聲明、求解的技術(shù)細(xì)節(jié)和向下蛔屹、向上取整及邊界條件削樊。

4.1 最大子數(shù)組問題

問題描述:一個記錄每天股票價格的數(shù)組,尋找一段日期兔毒,使得從第一天到最后一天股票價格凈變值最大漫贞。

分析:第一個想法:最低價格買進(jìn),最高價格賣出時收益最大育叁,但是最高價可能比在最低價更早出現(xiàn)迅脐。那么最低價格買進(jìn)或者最高價格賣出呢?即:

  1. 尋找最高和最低價格
  2. 從最高價開始向左尋找之前的最低價豪嗽;從最低價開始向右尋找之前的最高價谴蔑。
  3. 取兩對價格中差值最大者。

有反例:

說明有時最大收益既不是在最低價時買進(jìn)龟梦,也不是最高價賣出隐锭。

暴力法:嘗試每對可能的買進(jìn)、賣出的日期組合计贰,Ω(n^2)的復(fù)雜度钦睡。

問題變換

不從每日價格的角度看待輸入,而是考察每日價格變化躁倒,即當(dāng)天和前一天的價格差荞怒。原輸入數(shù)組變?yōu)椋?/p>

那么問題轉(zhuǎn)化為尋找A的和最大的非空連續(xù)子數(shù)組——最大子數(shù)組洒琢。

使用分治法求解

首先將原問題分解為一些規(guī)模相近的子問題,即將原數(shù)組分兩半分別求解挣输。A 的任何連續(xù)子數(shù)組的位置必然是以下情況:

故可以遞歸求解前兩個纬凤,然后尋找跨中點(diǎn)的最大子數(shù)組,最后三者中取最大撩嚼。

找跨中點(diǎn)的最大子數(shù)組可在線性時間內(nèi)完成:找出A[i ... mid] 和 A[mid + 1 ... j]的最大子數(shù)組然后合并停士。與原問題不同在于:此子數(shù)組必須包含A[mid]。
偽代碼:

所以最大子數(shù)組問題分治算法偽代碼為:

C++實(shí)現(xiàn):

int a[20] = {0, 13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
struct node
{
    int mxl, mxr, sum;
};
node CrossSum(int a[], int low, int mid, int high)
{
    int i, j, sum, maxl, maxr;
    int leftsum = -INF;
    sum = 0;
    for (i = mid; i >= low; i --)//從中間向前遍歷完丽,故一定選中a[mid]
    {
        sum += a[i];
        if(sum > leftsum)
        {
            leftsum = sum;
            maxl = i;
        }
    }
    int rightsum = -INF;
    sum = 0;
    for (j = mid + 1; j <= high; j ++)//從中間向后遍歷
    {
        sum += a[j];
        if(sum > rightsum)
        {
            rightsum = sum;
            maxr = j;
        }
    }
    node x;
    x.sum = leftsum + rightsum;   //左右最大值之和
    x.mxl = maxl;
    x.mxr = maxr;
    //printf("insub %d %d %d\n", x.mxl, x.mxr, x.sum);
    return x;
}
node FindMaxSub(int a[], int low, int high)
{
    node x, y, z;
    int i , j, mid;
    if (low == high)
    {
        x.mxl = low;
        x.mxr = high;
        x.sum = a[low];
        return x;
    }
    mid = (low + high) / 2;
    //printf("mid = %d\n", mid);
    x = FindMaxSub(a, low, mid);
    y = FindMaxSub(a, mid + 1, high);
    z = CrossSum(a, low, mid, high);
    //printf("l = %d %d %d\n", x.mxl, x.mxr, x.sum);
    //printf("r = %d %d %d\n", y.mxl, y.mxr, y.sum);
    //printf("cro = %d %d %d\n", z.mxl, z.mxr, z.sum);
    if (x.sum >= y.sum && x.sum >= z.sum)
        return x;
    else if (y.sum >= x.sum && y.sum >= z.sum)
        return y;
    else if (z.sum >= y.sum && z.sum >= x.sum)
        return z;
}

分治算法的分析

建立以上算法的遞歸式恋技。首先第一行基本情況T(1) = Θ(1)。n > 1時遞歸情況分兩半每份T(n/2)逻族,共2 * T(n/2)蜻底。6~11行子函數(shù)Θ(n),其余Θ(1)聘鳞。故此部分共2*T(n/2) + Θ(n)薄辅。得到遞歸式:

與歸并排序的一樣,故也為Θ(nlgn)抠璃。

練習(xí)

4.1-1

答:返回A中最大的單個元素max(A[i])

4.1-2

偽代碼:

FIND-MAX-SUBARRAY(A, low, high)
  left = 0
  right = 0
  sum = -∞
  for i = low to high
      current-sum = 0
      for j = i to high
      current-sum += A[j]
      if sum < current-sum
          sum = current-sum
          left = i
          right = j
  return (left, right, sum)
4.1-3

暴力算法C++實(shí)現(xiàn):

node FindMaxSub(int a[], int low, int high)
{
    int i, j, right = 0, left = 0;
    int sum = -INF;
    for (i = low; i <= high; i ++)
    {
        int curs = 0;
        for ( j = i; j <= high; j++)
        {
            curs += a[j];
            if (sum < curs)
            {
                sum = curs;
                left = i;
                right = j;
            }
        }
    }
    node x;
    x.sum = sum;
    x.mxl = left;
    x.mxr = right;
    return x;
}

暴力法 T(n) = a * n^2, 遞歸法 R(n) = b * nlgn站楚,比較可得交叉點(diǎn)。改后不會變搏嗡,相當(dāng)于合并圖像時取兩段較低的部分窿春。

4.1-4

答:每次返回子數(shù)組之前判斷和是否小于0,若是則返回sum = 0采盒。

4.1-5

答:用動態(tài)規(guī)劃的思想實(shí)現(xiàn)旧乞。若當(dāng)前和小于0則置0重新計(jì)算。C++代碼:

node FindMaxSub(int a[], int low, int high)
{
    int i, j, right = 0, left = 0;
    int sum = -INF, curs = 0;
    for (i = low; i <= high; i ++)
    {
        curs += a[i];
        if (curs > sum)
        {
            sum = curs;
            right = i;
        }
        if (curs < 0)
        {
            curs = 0;
            left = i + 1;
        }
    }
    node x;
    x.sum = sum;
    x.mxl = left;
    x.mxr = right;
    return x;
}



4.2 矩陣乘法的Strassen算法

矩陣乘法公式為:

需要計(jì)算n^2個元素磅氨,每個元素是 n 個數(shù)的和尺栖。根據(jù)公式偽代碼:
先遍歷每行,再遍歷每列烦租,對每個元素按公式計(jì)算求和决瞳。復(fù)雜度 Θ(n ^3)。

簡單分治法

將每個矩陣分四份

根據(jù)矩陣公式有

按此設(shè)計(jì)的分治算法偽代碼:

第五行分解矩陣若創(chuàng)建12個新矩陣將花費(fèi)Θ(n ^2)左权。通過原矩陣的一組行皮胡、列下標(biāo)指定子矩陣可以不用復(fù)制,故第五行可在Θ(1)內(nèi)實(shí)現(xiàn)赏迟。

  • n = 1時 T(1) = Θ(1)
  • n > 1時 為遞歸情況屡贺,共8 次遞歸調(diào)用,每次完成兩個n/2 * n/2的子矩陣乘法時間 8T(n/2), 矩陣加法總時間Θ(n ^2)甩栈⌒合桑總時間

得到遞歸式:

解出來實(shí)際上還是復(fù)雜度 Θ(n ^3)。

Strassen方法

核心思想是讓遞歸樹不那么茂盛量没,遞歸調(diào)用減為7次玉转。步驟為:

遞歸式:
用主方法得到復(fù)雜度為Θ(n ^lg7)。


4.3 用代入法求解遞歸式

代入法求解分兩步:

  1. 猜測解的形式
  2. 用數(shù)學(xué)歸納法求出解中的常數(shù)殴蹄,并證明正確性究抓。

例:確定下式上界

猜測為O(n lg n),證明對于c > 0有T(n) <= c * nlgn袭灯。假定對所有證書m < n成立刺下,m = floor(n/2)時问欠,帶入猜測式:

擴(kuò)展邊界條件使歸納假設(shè)對較小的n成立可帽。至于好的猜測,可通過遞歸樹總結(jié)展鸡,或者先證明遞歸式較松的上下界姨丈,然后縮小不確定范圍畅卓。一些技巧:

  1. 歸納假設(shè)不夠強(qiáng),無法證出準(zhǔn)確的界時蟋恬,可以試著從先前的猜測減去一個低階項(xiàng)翁潘。
  2. 改變變量,函數(shù)整體替換筋现。

練習(xí)

4.3-1

答:猜測T(n) <= cn^2 則
4.3-2
4.3-3

4.4 用遞歸樹解遞歸式

畫出遞歸樹是做出好的猜測的簡單直接方法。在遞歸樹中箱歧,每個結(jié)點(diǎn)代表一個單一子問題的代價矾飞。將樹中每層代價求和,將所有層次代價求和得到遞歸調(diào)用總代價呀邢。

4.5 用主方法求解遞歸式

主方法為如下的遞歸式提供了“菜譜”式的求解方法洒沦。

主定理:

對于遞歸式

則不能用,因?yàn)閒(n)并不是多項(xiàng)式意義上的大于价淌,遞歸式在情況2~3之間申眼。

練習(xí)

4.5-1

代入主定理
4.5-2
4.5-4

答:不能,原因同上特例蝉衣。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末括尸,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子病毡,更是在濱河造成了極大的恐慌濒翻,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異有送,居然都是意外死亡淌喻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門雀摘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裸删,“玉大人,你說我怎么就攤上這事阵赠⊙乃” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵豌注,是天一觀的道長伤塌。 經(jīng)常有香客問我,道長轧铁,這世上最難降的妖魔是什么每聪? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮齿风,結(jié)果婚禮上药薯,老公的妹妹穿的比我還像新娘。我一直安慰自己救斑,他們只是感情好童本,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著脸候,像睡著了一般穷娱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上运沦,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天泵额,我揣著相機(jī)與錄音,去河邊找鬼携添。 笑死嫁盲,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的烈掠。 我是一名探鬼主播羞秤,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼左敌!你這毒婦竟也來了瘾蛋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤矫限,失蹤者是張志新(化名)和其女友劉穎瘦黑,沒想到半個月后京革,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幸斥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年匹摇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甲葬。...
    茶點(diǎn)故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡廊勃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出经窖,到底是詐尸還是另有隱情坡垫,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布画侣,位于F島的核電站冰悠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏配乱。R本人自食惡果不足惜溉卓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搬泥。 院中可真熱鬧桑寨,春花似錦、人聲如沸忿檩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽燥透。三九已至沙咏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間班套,已是汗流浹背肢藐。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留孽尽,地道東北人窖壕。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓忧勿,卻偏偏與公主長得像杉女,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鸳吸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容