算法导论阅读笔记2-分治算法

分治算法的三个主要步骤:

  • 分:将问题划分为数个子问题,每个子问题是该问题的更小实例。
  • 治:通过递归迭代处理子问题。然而,如果子问题的规模足够小,直接处理子问题。
  • 组合:组合子问题的解构成原始问题的解。

求解递归表达式(如T(n) = 2T(n/2) + θ(n))的三种方法:

  • 替代法:猜测表达式的界,并使用数学归纳法证明猜测的准确性;
  • 递归树方法
  • 主方法:给出如下格式的递归表达式的界T(n) = aT(n/b) + f(n),其中a≥1, b>1, 且f(n)为给定函数。

最大子数组问题(maximum subarray problem)

最大子数组问题只有在数组中存在负值时才有意义。否则,整个数组是最大子数组。

问题描述

给定数组A,寻找A的和最大的非空连续子数组。

解决方法

假定我们要寻找子数组A[low..high]的最大子数组。使用分治技术意味着将子数组划分为两个规模尽量相等的子数组,即找到数组的中心位置,比如mid。然后,考虑求解两个子数组A[low..mid]和A[mid + 1..high]。如图所示,A[low..high]的任何连续子数组A[i..j]所处的位置必然是以下三种情形之一。

  • 完全位于子数组A[low..mid]中,因此low≤i≤j≤mid。
  • 完全位于子数组A[mid+1..high]中,因此mid+1≤i≤j≤high。
  • 跨越中心点,因此low≤i≤mid≤j≤high。


    子数组的可能位置

因此,我们可以递归的求解A[low..mid]和A[mid + 1..high]的最大子数组。剩下的问题是寻找跨越中心点位置的最大子数组。然后,选取三种情况中的和最大者。

任何跨越中心点的子数组都由两个子数组A[i..mid]和A[mid+1..j]组成。因此,我们只需找出A[i..mid]和A[mid+1..j]的最大子数组,然后将其合并即可。

FIND-MAX-CROSSING-SUBARRAY(A, low, mid, high)
left-sum = -INF
sum = 0
for i = mid downto low
  sum = sum + A[i]
  if sum > left-sum
    left-sum = sum
    max-left = i
right-sum = -INF
sum = 0
for j = mid + 1 to high
  sum = sum + A[j]
  if sum > right-sum
    right-sum = sum
    max-right = j
return (max-left, max-right, left-sum + right-sum)

FIND-MAXIMUM-SUBARRAY(A, low, high)
if high == low
  return (low, high, A[low])
else mid = (low+high)/2
  (left-low, left-high, left-sum) = FIND-MAXIMUM-SUBARRAY(A, low, mid)
  (right-low, rigth-high, right-sum) = FIND-MAXIMUM-SUBARRAY(A, mid + 1, high)
  (cross-low, cross-high, cross-sum) = FIND-MAX-CROSSING-SUBARRAY(A, low, mid, high)
  if left-sum >= right-sum and left-sum >= cross-sum
    return (left-low, left-high, left-sum)
  elseif right-sum >= left-sum and right-sum >= cross-sum
    return (right-low, right-high, right-sum)
  else
    return (cross-low, cross-high, cross-sum)

c代码

struct result {
  int left;
  int right;
  int sum;
};
struct result find_max_crossing_subarray(int *a, int low, int mid, int high) {
  int left_sum = INT_MIN;
  int sum = 0
  int max_left = -1;
  for(int i = mid; i >= low; i--) {
    sum = sum + a[i];
    if (sum > left-sum) {
      left_sum = sum;
      max_left = i;
    }
  }
  int right_sum = INT_MIN
  sum = 0
  int int max_right = -1;
  for (int j = mid + 1; j <= high; j++) {
    sum = sum + a[j];
    if (sum > right_sum) {
      right_sum = sum;
      max_right = j;
    }
  }
  struct result res;
  res.left = max_left;
  res.rigth = max_right;
  res.sum = left_sum + right_sum;
  return res;
}

struct result find_maximum_subarray(int *a, int low, int high) {
  struct result res;
  if (high == low) {
    res.left = low;
    res.right = high;
    res.sum = a[low];
    return res;
  }
  else {
    int mid = (low+high)/2
    struct result res1 = find_maximum_subarray(a, low, mid);
    struct result res2 = find_maximum_subarray(a, mid+1, high);
    struct result res3 = find_max_crossing_subarray(a, low, mid, high);
    if (res1.sum >= res2.sum && res1.sum >= res3.sum) {
      return res1;
    }
    elseif (res2.sum >= res1.sum && res2.sum >= res3.sum) {
      return res2;
    }
    else
      return res3;
  }
}

算法特性

  • 时间复杂度为θ(nlgn)。

矩阵乘法的Strassen算法

按照矩阵乘定义进行计算

SQUARE-MATRIX-MULTIPLY(A, B)
n = A.row
let C be a new n*n matrix
for i = 1 to n
  for j = 1 to n
    cij = 0
    for k = 1 to n
      cij = cij + aij * bij
return C

时间复杂度为θ(n3)。

直观分块算法

矩阵分块

矩阵分块乘法

矩阵分块乘法计算公式

伪代码

SQUARE-MATRIX-MULTIPLY-RECURSIVE(A,B)
n = A.rows
let C be a new n*n matrix
if n == 1
  c11 = a11*b11
else partition A, B, and C
  C11 = SQUARE-MATRIX-MULTIPLY_RECURSIVE(A11, B11)
      + SQUARE-MATRIX-MULTIPLY_RECURSIVE(A12, B21)
  C12 = SQUARE-MATRIX-MULTIPLY_RECURSIVE(A11, B12)
      + SQUARE-MATRIX-MULTIPLY_RECURSIVE(A12, B22)
  C21 = SQUARE-MATRIX-MULTIPLY_RECURSIVE(A21, B11)
      + SQUARE-MATRIX-MULTIPLY_RECURSIVE(A22, B21)
  C22 = SQUARE-MATRIX-MULTIPLY_RECURSIVE(A21, B12)
      + SQUARE-MATRIX-MULTIPLY_RECURSIVE(A22, B22)
return C

T(n) = 8T(n/2) + θ(n2)
时间复杂度为θ(n3)

Strassen算法

核心思想是令递归树稍微不那么浓密,即只递归7次而不是8次n/2xn/2矩阵的乘法。减少一次矩阵乘法带来的代价可能是额外几次n/2xn/2矩阵的加法,但只是常数次。
该算法包含4个步骤:

  • 将输入矩阵A、B和输出矩阵C分解为n/2xn/2的子矩阵。此步骤花费θ(1)时间,与SQUARE-MATRIX-MULTIPLY-RECURSIVE相同。
  • 创建10个n/2xn/2的矩阵S1, S2, ..., S10,每个矩阵保存步骤1中创建的两个子矩阵的和或差。花费时间为θ(n2)。
  • 用步骤1中创建的子矩阵和步骤2中创建的10个矩阵,递归地计算7个矩阵积P1, P2, ..., P7。每个矩阵Pi都是n/2xn/2的。
  • 通过Pi矩阵的不同组合进行加减运算,计算出结果矩阵C的子矩阵C11, C12, C21, C22。花费时间θ(n2)。
    递归式:T(n) = 7T(n/2) + θ(n2)。
    时间复杂度: T(n) = θ(nlg7)(笔者注:O(n2.81))

算法步骤

在步骤2中,创建如下10个矩阵
S1 = B12 - B22
S2 = A11 + A12
S3 = A21 + A22
S4 = B21 - B11
S5 = A11 + A22
S6 = B11 + B22
S7 = A12 - A22
S8 = B21 + B22
S9 = A11 - A21
S10 = B11 + B12

在步骤3中,递归地计算7次n/2xn/2矩阵的乘法,如下所示:
P1 = A11xS1
P2 = S2xB22
P3 = S3xB11
P4 = A22xS4
P5 = S5xS6
P6 = S7xS8
P7 = S9xS10

步骤4:
C11 = P5 + P4 - P2 + P6
C12 = P1 + P2
C21 = P3 + P4
C22 = P5 + P1 - P3 - P7

主定理

令a≥1和b>1是常数,f(n)是一个函数,T(n)是定义在非负整数上的递归式:T(n) = aT(n/b) + f(n)
其中我们将n/b解释为\lfloor{n/b}\rfloor\lceil{n/b}\rceil。那么,T(n)有如下渐进界:

  1. 若对某个常数\epsilon>0f(n)=O(n^{log_{b}a-\epsilon}),那么T(n) = \Theta(n^{log_{b}a})
  2. f(n)=O(n^{log_{b}a}),那么T(n) = \Theta(n^{log_{b}a}lgn)
  3. 若对某个常数\epsilon>0f(n)=\Omega(n^{log_{b}a+\epsilon}),且对某个常数c<1和所有足够大的n有af(n/b) \le cf(n),则T(n) = \Theta(f(n))
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • 算法和数据结构 [TOC] 算法 函数的增长 渐近记号 用来描述算法渐近运行时间的记号,根据定义域为自然数集$N=...
    wxainn阅读 1,054评论 0 0
  • 1 序 2016年6月25日夜,帝都,天下着大雨,拖着行李箱和同学在校门口照了最后一张合照,搬离寝室打车去了提前租...
    RichardJieChen阅读 5,073评论 0 12
  • 对职场小白来说,认真讲,在工作的最前期,也只能就完成最基层最基础的工作,怎么谈看世界这么奢侈的梦想。 说这...
    哈梅林whistling阅读 292评论 0 0
  • 去年的现在,也就是在这个美丽的季节碰到了我职业生涯里的第一个师傅。 “日有所思,夜有所梦”昨夜梦到了她,这场梦...
    冷静的发呆的林子阅读 1,892评论 9 8