分治策略学习笔记

以下的东西来自中国大学MOOC北京大学屈婉玲老师的算法设计与分析课程,本人只是做了一下搬运,中间如果有什么错误,纯属本人的失误。慕课地址

分治策略的设计思想

将一个大问题分割成多个子问题,对子问题逐个求解,最终将所有子问题的结果汇总起来,得到大问题的解。

分治策略的一般性描述

Devide-and-Conquer( P )

  1. if |P| <= c then S( P )
  2. divide P into P 1 P_1 P1, P 2 P_2 P2, …, P k P_k Pk
  3. for i ← 1 i←1 i1 to k
    .         y i ← y_i← yiDivide-andConquer( P i P_i Pi)
  4. Return Merge( y 1 , y 2 , y 3 y_1, y_2, y_3 y1,y2,y3 …, y k y_k yk)

其中,第一步,判断当前问题P的规模能否直接进行求解,如果可以就直接进行求解。即当问题P的规模小于c时,使用方法S()对问题求解。
第二步,将问题P分割为k个规模更小的子问题。
第三步,逐个对问题P分割出来的更小的子问题 P k P_k Pk进行求解,这是一个递归调用的过程。
第四步,将所有的子问题的解合并,返回最终结果。

设计要点

1、原问题可以划分或者规约为规模较小的子问题。其中,子问题要与原问题有相同的性质;子问题的各个解之间彼此独立;划分时子问题的规模要尽可能的均衡。

2、子问题的规模小到一定的程度的时候可以直接进行求解。

3、子问题的解综合之后可以得到原问题的解。

在分治策略中有以下几个问题:

1、子问题 P i P_i Pi划分成多大规模的时候可以进行求解,这个标准是多少。

2、子问题 P i P_i Pi如果可以进行求解了,怎样进行求解,要有一个通用的方法,能够得到正确的答案。

3、问题P如何进行分割,标准是什么。

4、问题P在得到其所有子问题的解之后,如何进行合并得到P的解。

例题

芯片测试

有N个芯片,其中好的芯片总要比坏的芯片至少多一片。
好的芯片测试结果一定是正确的,坏的芯片测试结果不确定,可能是好的也可能是坏的。

那么对于一次芯片测试,有以下的测试结果

A报告B报告结论
B是好的A是好的A/B都是好的或者都是坏的
B是好的A是坏的A/B至少有一片是坏的
B是坏的A是好的A/B至少有一片是坏的
B是坏的A是坏的A/B至少有一片是坏的

问题

输入:N片芯片,好的芯片至少要比坏的多一片。

问题:设计一种方法,从N个芯片中找到一个好的芯片。

要求:使用最少的次数。

分析:

在输入n为奇数时,至少有 ( n + 1 ) 2 \frac {(n+1)}{2} 2(n+1)片芯片是好的,有 ( n − 1 ) 2 \frac {(n-1)}{2} 2(n1)片芯片是坏的。
然后取其中一片芯片依次和其他的芯片进行测试。如果这个芯片是好的,那么剩下的好的芯片就只有 ( n − 1 ) 2 \frac {(n-1)}{2} 2(n1)片,测试结果也就是至少会有 ( n − 1 ) 2 \frac {(n-1)}{2} 2(n1)个测试结果是好。如果芯片是坏的,那么剩余的芯片中就会有 ( n + 1 ) 2 \frac {(n+1)}{2} 2(n+1)个好芯片。然后就会有至少 ( n + 1 ) 2 \frac {(n+1)}{2} 2(n+1)个测试结果是坏。根据这个就可以判断出来这个芯片是好还是坏。

当输入为偶数时,至少会有 n 2 + 1 \frac {n}{2}+1 2n+1片芯片是好的,然后同上面的逻辑,就又可以得到这个芯片是好还是坏。
另外就是,将所有的芯片两两进行对比,只取结测试结果均为好的一次测试中的一枚芯片,这样可以保证取到的结果集中,扔可以保证好的芯片的数量至少比坏的芯片多一。

蛮力算法

直接取一个芯片同剩余的芯片进行对比,若这枚芯片为好的,就返回;不好就将这片芯片扔掉,继续进入下一次的判断,直到得到所有的结果。

时间复杂度为 O ( n 2 ) O(n^2) O(n2)

分治算法

如果n的数量为偶数,就两两一组进行对比,只取结果均为好的那一组中的一个。
如果n为奇数,仍然两两一组进行对比,对轮空的那个单独进行对比。

这样直到n的数量小于等于3,
n=3,任取其中一组进行对比,如果结果均为好,那就随便返回其中的一个,如果不是,就返回还未对比的那个。
n<3,任取一个返回结果。

快速排序

基本思想

使用首元素x作为基准,将数组分为比x大的,比x小的两部分。x就在他们中间,然后依次对剩余的两部分进行递归调用,最后就得到结果了。

其中在最好的情况下,也就是每次都是均分数组时间复杂度为 O ( n log ⁡ 2 n ) O(n\log _2 n) O(nlog2n)
最坏的情况下,每次都是0/n-1进行划分,这样时间复杂度为 O ( n ( n − 1 ) 2 ) O(\frac {n(n-1)}{2}) O(2n(n1))
另外,只要是按照某个特定比例进行划分,时间复杂度仍然为 O ( n log ⁡ 2 n ) O(n\log _2 n) O(nlog2n)

平均时间复杂度为 O ( n log ⁡ 2 n ) O(n\log _2 n) O(nlog2n)

幂乘问题

正常情况下
a n = a × a × ⋯ × a a^n=a \times a \times \cdots \times a an=a×a××a
这一共乘了 n n n次,自然而然,时间复杂度为 O ( n ) O(n) O(n)

使用分治算法之后
a n = { a n 2 × a n 2 n 为偶数 a n − 1 2 × a n − 1 2 × a n 为奇数 a^n = \begin {cases} {a^{\frac{n}{2}} \times a^{\frac{n}{2}}}& \text{$n$为偶数}\\\\ {a^{\frac{n-1}{2}} \times a^{\frac{n-1}{2}} \times a}& \text{$n$为奇数} \end{cases} an=a2n×a2na2n1×a2n1×an为偶数n为奇数
这个时候子问题的规模就变成 n 2 \frac{n}{2} 2n了,而分成的两个子问题又一样,只需要计算一次即可。

整个算法的时间复杂度为 O ( log ⁡ 2 n ) O(\log_2 n) O(log2n)

Fibonacci数列

对于Fibonacci数列有以下地推公式:
F n = F n − 1 + F n − 2 F_n = F_{n-1}+F_{n-2} Fn=Fn1+Fn2

按照这个公式设计算法,时间复杂度为 O ( n ) O(n) O(n)

对于Fibonacci数列又有以下的定理:
[ F n + 1 F n F n F n − 1 ] = [ 1 1 1 0 ] n \begin{bmatrix} F_{n+1} & F_{n} \\ F_{n} & F_{n-1} \end{bmatrix} = {\begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}}^n [Fn+1FnFnFn1]=[1110]n

具体的证法自己去百度,用的是归纳法。

使用这个公式,就可以在 O ( log ⁡ 2 n ) O(\log_2 n) O(log2n)次矩阵运算后得到结果。每次矩阵运算要进行8次乘法计算,所以整个算法的时间复杂度为 O ( log ⁡ 2 n ) O(\log_2 n) O(log2n)

Fibonacci数列 java实现

对分治算法的优化

分治算法的时间复杂度方程:
W ( n ) = a W ( n b ) + d ( n ) W(n) = aW( \frac{n}{b}) + d(n) W(n)=aW(bn)+d(n)
其中:

  • a a a:子问题个数
  • n b \frac{n}{b} bn子问题规模
  • d ( n ) d(n) d(n):划分与综合的工作量

而对分治算法的优化也主要集中在对 a a a的减小或者是对 d ( n ) d(n) d(n)的减小上。

减少子问题个数

a a a较大, b b b较小, d ( n ) d(n) d(n)不大时,方程的解为: W ( n ) = O ( n log ⁡ b a ) W(n)=O(n^{\log_b a}) W(n)=O(nlogba)

由此可以看出,减少子问题的个数是降低 W ( n ) W(n) W(n)的复杂度的一种途径。

主要方法就是通过问题之间的依赖关系,使子问题的解可以通过组合其他子问题的解得到。

增加预处理

这个方法就是通过提前对输入集进行处理,将每一个子问题都需要进行的处理提出来,先进行处理,这样就减小了 d ( n ) d(n) d(n)借此,减小算法的时间复杂度。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值