遞迴程式的漸近分析(以分治為例)

漆楚衡發表於2014-11-21

分治演算法的一般形式:將規模為n的問題按照每次分為k個規模為(n / m)的子問題求解,合併成本為f(n):

約定:

  • log(a, b)表示以a為底求b的對數
  • 以下分析沒有嚴格區分是否是否算上最後一行

一般地,有:

  • T(n) = O(1) n = 1

  • T(n) = k * T(n / m) + f(n) n > 1

部分1:只看k * T(n / m)

  • 可以發現會巢狀log(m, n)次
  • 即有計算次數:t = k ^ log(m, n)
  • 因為對於一個演算法,k一般為常數,我們希望這個公式更明顯地表徵n
  • 於是有:t = n ^ log(m, k)
  • 從中可以看出部分1主要取決於分割的效率
  • m(n / m表示子問題規模)與k(子問題的數量)的關係

    • m < k,表示劃分子問題時有冗餘
    • m = k,可以表示分割處理可以剛好對問題無重無漏的劃分
    • m > k,即問題被分成了m份,我們只去處理其中的k份

部分2:對於f(n),從一個直觀角度考慮:

  • 想象問題的處理過程為一顆樹
  • 設最初的問題為樹根(第0層)
  • 對其分治後有k個子問題,即有k棵子樹(每棵子樹的規模為n / m)
  • 遞迴定義子樹
  • 現在我們有一棵樹,它代表了整個處理過程(在漸近意義下,不使一般性,設其為一棵滿k叉樹)
  • 我們一層一層看
  • 對於第i層,共有k ^ i個節點,單個節點的規模為n / m ^ i
  • 於是,每一層的計算成本是k ^ i * f(n / m ^ i)
  • 按行加和可得整體成本

彙總:

  • 部分1就是整個遞迴完全展開中與f(n)無關的部分,是按分治樹中每個節點計算成本為常數來計算,可以理解為樹的大小
  • 部分2就是整個遞迴完全展開中與f(n)有關的部分,分治樹中只在每一層節點有相同的權值
  • 可以看出來,f(n)很重要,只有當它與規模無關(O(1))時才能不增加演算法的複雜性

例子:

  • 二分查詢:

    • 因為二分查詢總是將解均分為兩半並丟棄一半,即其分治樹退化為線性表
    • 有m = 2, k = 1
    • 而且沒有合併需求,視f(n)為O(1)
    • 於是有:
    • 二分查詢的複雜度為:O(log n)
    • 注意:當k = 1時,分析1所示的部分已經失效了,需要規定f(n)至少為O(1)(即f(n)總是存在)來至少表示樹的大小
  • 並歸排序

    • m = k = 2
    • f(n) 為O(n)
    • 考察任一層i的成本
    • 單個節點的成本為f(n / m ^ i) = O(n / m ^ i)
    • 共有k ^ i個節點
    • 每一層的總合併成本總是O(n / 2 ^ i) * O(2 ^ i) = O(n)
    • 共有log(2, n) - 1層需要合併
    • 漸進地有O(n * log(n))

相關文章