4算法设计与分析笔记(Authored by M.H Alsuwaiyel, Saudi)

第四章 分治策略

  • 为解决问题P,可以将P分解为P_1,P_2,...,P_k等子问题,通过解决子问题再组合得到问题P的解。(子问题是一种递归思想)

寻找最大值最小值MINMAX

  • 对于数组A[1···n]最简单的是一次遍历得到MinMax,比较次数为2n-2(不展开说明);利用分治策略,我们只需要{3n\over 2}-2次比较就可以得到结果。
  • 思路是:每次将A分为两半,在每一半中找到最大值和最小值,并返回两个最小值的最小值,两个最大值的最大值,将大问题逐步分解为小问题。

MINMAX

输入:n个整数元素的数组A[1···n],n为2的幂。

输出:(x, y),A中的最大元素和最小元素。

minmax(1, n)
过程 minmax(low, high)
if high - low = 1 then #此时子问题是两个数的比较,易得min和max
    if A[low] < A[high] then return (A[low], A[high])
    else return (A[high], A[low])
    end if
else
    mid <- (low + high) / 2
    (x1, y1) <- minmax(low, mid)
    (x2, y2) <- minmax(mid+1, high) #分别递归处理左半边和右半边
    x <- min{x1, x2} #仅在line10、line11进行元素比较
    y <- max{y1, y2}
    return (x, y)
end if
  • C(n)表示算法在数组A[1···n]上的比较次数,元素的比较仅发生在line10~line11,而递归调用的比较次数是2*C(n/2),即:

    C(n)=\begin{cases}1,n-2\\ 2C(n/2)+2,n>2 \end{cases}

    C(n)=2C(n/2)+2=2(C(n/4)+2)+2=···=2^{k-1}C(n/{2^{k-1}})+2^{k-1}+2^{k-2}+···+2^2+2

    C(n)=2^{k-1}C(2)+\sum_{j=1}^{k-1}2^j=3n/2-2

合并排序MERGESORT

  • 思路是:将n个元素的数组分为两个n/2的子数组,递归分解并对子数组排序,最后将两个子数组递归合并就可以得到最终的有序序列。

MERGESORT

输入:n个元素的数组A[1···n]。

输出:按非降序排列的数组A[1···n]。

mergesort(A, 1, n)
过程 mergesort(low, high)
if low < high then
    mid <- (low + high) / 2
    mergesort(A, low, mid)
    mergesort(A, mid+1, high)
    MERGE(A, low, mid, high)
end if

  • 时间复杂度分析:分解步骤仅仅计算mid,需要常量时间\Theta(1);解决步骤需要递归解决2个规模为n/2的子问题,贡献2T(n/2);合并步骤的问题总规模为n,根据MERGE算法可知贡献时间\Theta(n)

    T(n)=\begin{cases} \Theta(1),n=1\\ 2T(n/2)+\Theta(n),n>1 \end{cases}

    计算得:MERGESORT算法的时间复杂度为\Theta(nlogn)

寻找中项和第k小的元素SELECT

  • 对于已经排序好的(非降序)数组A[1···n]n为奇数,中项为第(n+1)/2个元素;n为偶数,中项为第n/2个元素;综上,中项为第\lceil n/2 \rceil个元素。寻找中项可以先排序再取中项,但时间复杂度较高(\Theta(nlogn))。

  • 寻找中项是寻找第k小元素的一个特例,下面我们解决寻找第k小元素的问题。

  • 思路是:如果元素个数小于预定义的阈值44,我们把n个元素划分为\lfloor n/5 \rfloor组(不一定是五组,取决于阈值)。每组包含五个元素,如果n不是5的倍数,则排除剩余元素,这应当不影响算法的执行,对每组进行排序得到其中项元素,然后将这些中项中的中项记为mm;然后依据mm将数组A分为大于、小于、等于三个子数组,判断第k小的元素在哪一部分,返回或者在子数组内递归

SELECT

输入:n个元素的数组A[1···n]和整数k。

输出:A中的第k小元素。

select(A, 1, n, k)
过程 select(A, low, high, k)
p <- high - low + 1
if p < 44 then 将A排序return(A[k]) #当元素个数小于阈值时,排序取中项的时间更优
令q <- p / 5。将A分为q组,每组5个元素。如果5不整除p,则排除剩余元素。
将q组中的每一组单独排序,找出中项。所有中项集合记为M。
mm <- select(M, 1, q, q/2) #中项的中项实际上也是寻找第k小元素问题
将A[low···high]分成三组
A1 = {a|a < mm}
A2 = {a|a = mm}
A3 = {a|a > mm}
case #|A|指示数组A的元素个数
|A1| >= k:return select(A1, 1, |A1|, k) #通过mm将A分割后,若|A1| < k则该元素必然不会出现在A1内
|A1| + |A2| >= k:return mm
|A1| + |A2| < k:return select(A3, 1, |A3|, k-|A1|-|A2|)
end case

  • SELECT时间复杂度为\Theta(n)

快速排序QUICKSORT

  • 快速排序十分高效,具有\Theta(nlogn)的平均时间复杂度。它优于MERGESORT算法(合并排序的时间复杂度也为\Theta(nlogn))的一点是:它在原位上排序,即对于被排序的元素不需要额外的存储空间,在数据量很大时优势明显。

划分算法SPLIT

  • 对于数组A[low···high],并且令x=A[low],我们将A划分为大于x小于x两个部分,x称为主元拆分元素
  • 定义:如果元素A[j]既不小于其左边的元素,也不大于其右边的元素,则称A[j]在一个适当/正确的位置。显然,数组A通过x划分后,x处在一个适当的位置。

SPLIT

输入:数组A[low···high]。

输出:(1)如果有必要,输出重新排列的数组A;(2)划分元素A[low]的新位置w。

i <- low 
x <- A[low]
for j <- low + 1 to high #基准i和j分别向右移动,当需要交换时i才自增
    if A[j] <= x then #当A[j]需要移动到x右边时,基准加一
        i <- i + 1
        if i != j then swap(A[i], A[j])
    end if
end for
swap(A[low], A[i]) #最终基准i的位置即为x的适当位置
w <- i
return A和w

  • SPLIT算法比较只发生在line4时间复杂度为\Theta(n)

排序算法QUICKSORT

  • 在理解SPLIT算法的基础上,快速排序的思路就是:以A[low]对数组进行划分确定A[low]的适当位置,然后数组A被分为两个子数组,递归划分。

QUICKSORT

输入:n个元素的数组A[1···n]。

输出:按非降序排列的数组A[1···n]。

quicksort(A, 1, n)
过程 quicksort(A, low, high)
if low < high then
    SPLIT(A[low···high], w)
    quicksort(A, low, w-1)
    quicksort(A, w+1, high)
end if

大整数相乘

  • XY是两个n位的整数(二进制),传统的乘法算法 需要\Theta(n^2)数字相乘来计算它们的乘积(如下图)。而使用分治策略,时间复杂度能显著减小。

  • 我们把每个整数分为两个部分,每部分为n\over 2位,则XY可以重写为X=A2^{n/2}+B,~Y=C2^{n/2}+D,由于2^n做乘法运算相当于简单地左移n位,仅需要\Theta(n)的时间。

  • 时间复杂度分析:

    1. AC、AD、BC、BD为4次n/2位的乘法,时间为T(n/2)
    2. AC*2^n,左移n位,时间为\Theta(n)
    3. AD+CBn位加法,时间为\Theta(n)
    4. (AD+CB)2^{n/2},左移n/2位,时间为\Theta(n)
    5. 三个式子相加,时间为\Theta(n)

    所以T(n)=\begin{cases} a,n=1\\ 4T(n/2)+\Theta(n),n>1 \end{cases}

    T(n)=\Theta(n^2),与传统乘法并无改进,如何降低呢?

  • 我们考虑A·D+B·C=(A+B)(C+D)-AC-BDXY又可以化简为:AC2^n+((A+B)(C+D)-AC-BD)2^{n/2}+BD,这样乘法运算被简化为3n/2位的乘法和6次加减运算(AC、BD、(A+B)(C+D)注意AC、BD被重复利用)。

  • 改进后T(n)=\begin{cases} a,n=1\\ 3T(n/2)+\Theta(n),n>1 \end{cases}

    计算得到T(n)=\Theta(n^{log3})=\Theta(n^{1.59}),这是对传统方法的一个显著改进。

矩阵乘法

  • A、B是两个n\times n的矩阵,计算C=AB

传统算法

  • 传统方法中,利用线性代数知识:C(i,j)=\sum_{k=1}^n A(i,k)B(k,j)

  • 易知,算法需要n^3乘法运算、n^3-n^2次加法运算,时间复杂度为\Theta(n^3)

递归算法

  • 假定n=2^k,那么我们可以将A、B划分为4个大小为{n\over 2}*{n\over 2}的子矩阵

    A=\left\{ \begin{matrix} A_{11}~A_{12}\\ A_{21}~A_{22} \end{matrix} \right\}, B=\left\{ \begin{matrix} B_{11}~B_{12}\\ B_{21}~B_{22} \end{matrix} \right\}

    C=\left\{ \begin{matrix} {A_{11}B_{11}+A_{12}B_{21}} ~~{A_{11}B_{12}+A_{12}B_{22}}\\ {A_{21}B_{11}+A_{22}B_{21}}~~{A_{21}B_{12}+A_{22}B_{22}} \end{matrix}\right\}

    需要8n/2 * n/2的矩阵乘法,4n/2*n/2的矩阵加法,递推式如下:

    T(n)=\begin{cases} m,n=1\\ 8T(n/2)+4(n/2)^2a,n\geq2 \end{cases}=\begin{cases} m,n=1\\ 8T(n/2)+an^2,n\geq2 \end{cases}

    计算得T(n)=\Theta(n^3),递归并不产生更有效的算法。

STRASSEN算法

  • Strassen矩阵算法在于减少子矩阵相乘的次数,实际上该算法只进行了7n/2*n/2矩阵乘法、18n/2*n/2矩阵的加法。

  • 时间复杂度为\Theta(n^{log7})=\Theta(n^{2.81})

最近点对问题

  • 给定平面上的点集S|S|=nS内点的个数为n)。任意连接其中两点p、q,我们需要找到点集中的最短距离(d(p,q)=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2},欧几里得距离)。

  • 采用暴力算法依次计算全部的欧几里得距离,时间复杂度为\Theta(n^2),下面介绍一种时间复杂度为\Theta(nlogn)的算法。

    • 第一步,我们以x坐标增序对S的点排序,接着将S分割成两个子集S_l,S_r,使得|S_l|=\lfloor |S|/2 \rfloor,|S_r|=\lceil |S|/2 \rceil。通俗地理解,就是在x轴的中点画一条平行y轴的线。

    • 最近距离可能出现在S_l中,或者S_r中,或者一个点在S_l,一个点在S_r我们可以通过递归计算出集合S_l、S_r的最短距离\delta_l,\delta_r(\delta=min(\delta_l,\delta_r)),对于横跨两个子集的情况,我们如果采用暴力算法,最坏情况下时间并不能得到有效提升。

    • 我们再讲S_l,S_r依据\delta,划分为[mid-\delta,~mid+\delta]that's[S_l',S_r']若存在\delta'<\delta则一定在新的子区域T中。不是区域中所有的点需要比较,只需要比较距p(p\in S_l')小于\deltaq(q\in S_r')。假设\delta'<\delta,则存在两点p(p\in S_l'),q(q\in S_r')使得d(p,q)=\delta'

    • 易知,q必在右区域的\delta*2\delta的矩形上,依据鸽舍原理,在该矩形上最多存在6q点。所以T中的每个点至多进行6次距离计算O(6n)

    • 我们如何找到所需比较的6个点呢?将T集合内的点按y坐标递增排序。不难发现对于T中的每个p,与它y坐标轴递增的6个点比较。

    • 因而T(n)=\begin{cases} 1,n=2\\ 3,n=3\\ 2T(n/2)+O(nlogn),n>3 \end{cases},得T(n)=O(nlog^2n)。这并不是我们先前提到的界。我们注意到若能降低对T的排序耗时|T|log|T|=O(nlogn)\Theta(n),则算法的时间复杂度将为T(n)=\Theta(nlogn)。所以我们可以在初始阶段,对x坐标排序(存储在数组X)同时对y坐标进行排序(存储在数组Y),即可成功简化。

      T(n)=\begin{cases} 1,n=2\\ 3,n=3\\ 2T(n/2)+\Theta(n),n>3 \end{cases},计算得T(n)=\Theta(nlogn),归纳为算法CLOSESTPAIR。

算法CLOSESTPAIR

CLOSESTPAIR

输入:平面上n个点的集合S。

输出:S中两点的最小距离。

以x坐标增序对S中的点排序。
Y <- 以y坐标增序对S中的点排序。
delta <- cp(1, n)
过程 cp(low, high)
if high-low+1 <= 3 then 用暴力算法计算delta。
else
    mid <- (low + high) / 2
    x0 <- x(S[mid])
    delta_l <- cp(low, mid)
    delta_r <- cp(mid+1, high)
    delta <- min(delta_l, delta_r)
    k <- 0
    for i <- 1 to n #从Y集合中得到T集合
        if |x(Y[i])-x0| <= delta then
            k <- k + 1 #k指示T的大小
            T[k] <- Y[i]
        end if
    end for
    delta_ <- 2 * delta
    for i <- 1 to k-1 #两层for循环遍历T中的点
        for j <- i+1 to min{i+6, k} #内层循环找六个点(如果k更小则找k个点)
            if d(T[i], T[j]) < delta_ then delta_ <- d(T[i], T[j])
        end for
    end for
    delta <- min(delta, delta_)
end if
return delta
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容