Algorithm Design and Analysis - Divide-and-Conquer
Main message:
- 从最简单的case入手
- 看能否分,能否combine
- 不求最优,只要次优
一、Remarks
1.对于ClosestPair问题,若暴力破解法将花费多项式时间,分治法通常可被用于节省运行时间:\(O(n^2)\) \(\implies\) \(O(nlogn)\) 2.这个技巧若与随机技巧结合将非常有威力
在使用分治法之前,需要先检验输入(i.e.问题是否可以被分为结构相似,规模更小的子问题)以及输出(i.e.原问题的解是否能够用子问题的解组合而成)
二、Sort Problem
将长度为n的数组排序
INPUT: An array of n integers, say A[0..n-1];
OUTPUT: The items of A in increasing order.
方法1.将其分为n-1长度的数列和一个元素
第一种方法可以从最简单的case入手,从n=2,n=3,到逐步解决原问题
1 | InsertionSort( A, n ) |
最差的情况:A数组里的元素为逆序
时间复杂度: \(T(n)=T(n-1)+O(n)=O(n^2)\)
方法2.将其分为两个独立的子问题(MERGE SORT)
将数组\(A[0..n-1]\)分为两个数组\(A[0..\frac{n}{2}-1]\)以及\(A[\frac{n}{2}-1..n-1]\)
1 | MergeSort(A; l; r) //从l到r之间排序 |
时间复杂度:O(n)
三、迭代的时间复杂度的分析方法:
- Unrolling the recurrence 硬展开
- Guess and substitution 猜然后验证
- Master theorem
technique 1: Unrolling the recurrence
We have \(T(n) = 2T(\frac{n}{2}) + O(n) <= 2T(\frac{n}{2}) + cn\) for a constant c. Let unrolling a few levels to find a pattern, and then sum over all levels.
technique 2: Guess and substitution
Guess and substitution: guess a solution, substitute it into the recurrence relation, and justify that it works.
technique 3:Master theorem 主定理
Let T(n) be defined by \(T(n)=aT(\frac{n}{b})+O(n^d)\) for a > 1, b > 1 and d > 0, 其中n为问题规模,a为递推的子问题数量,\(\frac{n}{b}\)为每个子问题的规模(假设每个子问题的规模基本一样), 为递推以外进行的计算工作. then T(n) can be bounded by:
- If d < \(\log_b a\), then \(T(n)=O(n^{\log_b a})\);
- If d = \(\log_b a\), then \(T(n)=O(n^{\log_b a} \log n)\);
- If d > \(\log_b a\), then \(T(n)=O(n^d)\).
四、Counting Inversion Problem 数逆序对
To count inversions in an array of n integers
INPUT: An array $A[0..n]$ with n distinct numbers;
OUTPUT:the number of inversions. A pair of indices i and j constitutes an inversion if i < j butA[i] > A[j].
可使用分治法解决数逆序对问题,其中Divide和Conquer部分与之前排序问题相同,即将问题分为两个独立的子问题。Combine部分有两种策略。
策略1:若\(A[0..\frac{n}{2}-1]\)以及\(A[\frac{n}{2}..n-1]\)没有特殊的结构,则我们需要检查所有可能的配对\((i,j)\)去检查逆序。即若左右数都是任意的,就必须写For循环。
时间复杂度为:\(T(n)=2T(\frac{n}{2})+\frac{n^2}{4}=O(n^2)\)
策略2:若每一部分为升序,则相对容易计算逆序
1 | Sort-and-Count(A) |
时间复杂度为:\(T(n)=2T(\frac{n}{2})+O(n)=O(n\log n)\)
五、The general Divide and Conquer paradigm
Basic Idea
Many problems are recursive in structure, i.e., to solve a given problem, they call themselves several times to deal with closely related sub-problems. These sub-problems have the same form to the original problem but a smaller size.
Three Steps
- Divide a problem into a number of independent sub-problems;
- Conquer the subproblems by solving them recursively;
- Combine the solutions to the subproblems into the solution to the original problem.
Quick Sort algorithm
Divide according to a randomly-selected pivot.根据随机选取的轴进行分割。
1 | QuickSort(A) |
- 随机的操作使得这个算法变得简单而高效
- 然而,随机增加了分析的难度,我们不能保证取到每个问题的第\(\frac{n}{2}\)个元素
最差的情况:选取到了每个迭代中最大/最小的元素
\[T(n) = T(n - 1) + O(n) = O(n^2)\]
最好的情况:选取到了每个迭代中最中间的元素
\[T(n) = 2T(\frac{n}{2}) + O(n) = O(n\log n)\]
大多数情况:选取到了一个接近中心的轴。这种情况我们认为期望的运行时间仍为:
\[T(n) = O(n\log n)\]
Analysis
Observation 1: 对于任意i,j而言,\(A[i]\)和\(A[j]\)之间最多被比较一次。
Observation 2: 当处理包含\(A[i..j]\)的元素时,当且仅当\(A[i]\)或\(A[j]\)被选为枢轴时比较\(A[i]\)和\(A[j]\)。
Modified Quick Sort
在原算法上寻找一个好的分点(大于\(/frac{n}{4}\)小于\(/frac{3n}{4}\)的点)。它在所有项目都不同时有用,然而耗内存,且在比原算法更慢,因为当它偏离中心点时不运行。
1 | ModifiedQuickSort(A) |
比起中心轴,近中心轴更容易获得。获得一个中心点为轴的概率是\(frac{1}{n}\),而获得一个近中心轴的概率是\(frac{1}{2}\)。因此,找到一个近中心轴的期望时间为2n。此时我们不追求最优的pivot,只选择足够好。
1 | \\Lomuto’s implementation |
1 | \\Hoare’s implementation |
快速排序只在无重复元素时性能好,若有重复的元素,可用PARTITION算法来解决这个问题,即将数列分为三部分:比pivot大,与pivot相等,比pivot小。仅需对不等于pivot的partitions进行迭代排序。
Selection problem
找到数组中第K小的item
INPUT:An array A = [A0, A1,.., An_1], and a number k < n;
OUTPUT:The k-th smallest item in general case (or the median of A as a specical case).
若先将A排序再寻找第k个值,时间复杂度为\(O(nlog n)\)。相反地,若使用分治法,则有可能开发出更快的算法(e.g.deterministic linear algorithm by Blum et al.)。时间复杂度为\(O(n)\)。
1 | Select(A; k) |
How to choose a pivot?
对于pivot(中心元),我们有三种选择方式:
最差的选择:选取到了每个迭代中最小的元素(线性下降)
\[T(n) = T(n - 1) + O(n) = O(n^2)\]
最好的选择:选取到了每个迭代中最中间的元素(指数级下降)
\[T(n) = T(\frac{n}{2}) + O(n) = O(n)\]
好的选择:选取到了一个接近中心的pivot。(子问题最坏的情况也是指数级下降)
\[T(n) ≤ T((1 − ϵ)n) + O(n) ≤ cn + c(1 − ϵ)n + c(1 − ϵ)^2 + .... = O(n)\]
How to select a nearly-central pivot?
我们可以通过以下几种方式尝试获得一个完整集合的中间值:
- Selecting a central pivot via examining medians of groups
- Selecting a central pivot via randomly selecting an element
- Selecting a central pivot via examining a random sample
Strategy 1:BFPRT algorithm uses median of medians as pivot
SelectMedian(A) Line up elements in groups of 5 elements; 2. Find the median of each group; //cost \(\frac{6n}{5}\) time 3. Find the median of medians (denoted as M)through recursively running Select over the group medians; // \(T(\frac{n}{5})\) time 4. Use M as pivot to partition A into S_ and S+; //\(O(n)\) time 5. if |S_| = k - 1 then 6. return M; 7. else if |S_| > k - 1 then 8. return Select(S_; k); //at most \(T(\frac{7n}{10})\) time 9. else 10. return Select(S+; k - |S_| - 1); //at most \(T(\frac{7n}{10})\) time 11. end if
这种方法较耗内存,我们也可以使用原位替换的算法:
1 | Select(A; l; r; k) |
图解:
Strategy 2: QuickSelect algorithm randomly select an element as pivot
1 | QuickSelect(A, k) |
Basic idea: when selecting an element uniformly at random, it is highly likely to get a good pivot since a fairly large fraction of the elements are nearly-central.
The expected running time of QuickSelect: T(n) = O(n);
Strategy 3: Floyd-Rivest algorithm selects a pivot based on random samples
1 | Floyd-Rivest-Select(A; k) |
六、例题
note:一般来说,对于1维问题而言,我们可以将其分为两部分来解决问题。对于二维问题而言,我们可以将其分为横纵四个子问题来解决。但这并不是对所有情况都适用。
1. ClosestPair problem
Given a set of points in a plane, to find the closest pair.
INPUT: n points in a plane;
OUTPUT: The pair with the least Euclidean distance.
暴力做法的时间复杂度为\[T(n^2)\],存在很多的冗余。 由于将平面分为四个部分会使点的分布不均,我们将平面分割为内含均匀数量的点的两个平面,分别计算两个平面内的最小对点距离,再计算跨越两个平面的最小距离。 我们可以观察到,我们仅需检查中线周围以两个平面中较小的最小距离为宽度的区域就足够。
并且,我们不需要检查条形区域内的所有点。检查它周围的11个点已足够。 我们将两倍宽度的长条分解成格子,每个格子中最多包含一个点,否则表明之前计算出的最小距离有误。 我们对y轴进行排序,然后仅需将每个点与其周围11个点进行比对。
1 | ClosestPair(pl; :::; pr) |
Time-complexity: \[T(n) = 2T(\frac{n}{2}) + O(n log n) = O(n log^2 n)\].
为了是复杂度降到O(n),我们可以引入structure,使用merge将每一个子问题先排序。
实例: