Undecidable Problems 
罗素悖论(Russell’s Paradox)是一个很著名的悖论:设集合 S S S   是由一切不属于自身的集合所组成(即 S = { x ∣ x ∉ x } S=\{x|x \notin x\} S = { x ∣ x ∈ / x }  ),那 S S S   属于 S S S   吗?
我们通常用理发师悖论(The Barber paradox)去解释罗素悖论。假设城里只有一个理发师,且任何一个不能自己理发的人会由理发师帮他理发。定义 S ( x ) S(x) S ( x )   为所有被 x x x   理发的人的集合,则 S ( b a r b e r ) = { x ∣ x ∉ S ( x ) } S(barber)=\{x|x \notin S(x)\} S ( ba r b er ) = { x ∣ x ∈ / S ( x )}   。但是 b a r b e r ∈ S ( b a r b e r ) barber \in S(barber) ba r b er ∈ S ( ba r b er )   是否成立呢?成立和不成立都会导致矛盾。
经典的停机问题(Halting problem)就是从以上的悖论中构造出来的。于是我们用 Undecidable Problem 去描述那些不存在算法能正确判定 yes/no 的问题。判定两个 CFG 是否同构也是不能解的。
下面介绍一下 Post Correspondence Problem。给出一个 dominos 集合,每一个 domino 的上下部分各印有一个字符串,且可以翻转。现在要找一个有限的 domino 序列(每一种 domino 可以重复选),使得上下部分各自连成的字符串完全相同。左图展示了 4 4 4   种 domino,右图是一个可行排列。
那么是否存在一种算法能判定一个 PCP 实例的解的存在性?答案是否定的。当字符集 ≥ 2 \ge 2 ≥ 2   时,PCP 是一个 Undecidable Problem。PCP 甚至可以模拟图灵机的计算过程。
Merge-Insertion-Sort 
先从一个问题引入:有 N N N   个互不相同的数,最少需要几次比较才能排出它们之间的大小关系?
首先,基于比较  的排序算法的复杂度下界是  O ( N log  N ) O(N \log N) O ( N log  N )  。这个结论可以用决策树(Decision Tree)证明:总共有 N ! N! N !   种可能的序列,每次比较最多排除掉一半,所以至少要 O ( log  N ! ) = O ( N log  N ) O(\log{N!})=O(N\log N) O ( log  N ! ) = O ( N log  N )   次。
可是归并排序也只需 O ( N log  N ) O(N \log N) O ( N log  N )   次比较就能使数组有序,此问题不是解决了吗?并不是。注意,我们考查的是 精确步数 ,而非在大 O O O   意义下。目前,这个问题在很小的 N N N   下也未研究清楚。
一个经典且有效的策略是 Binary Comparison Sort :每次把第 k k k   个数二分插入前 k − 1 k-1 k − 1   个有序的数字里(其实就是插入排序)。总步数是 ∑ k = 1 N ⌈ log  2 k ⌉ \sum \limits_{k=1}^N \lceil \log_2k \rceil k = 1 ∑ N  ⌈ log  2  k ⌉  。
上述做法在 N = 5 N=5 N = 5   时就失败了。几乎所有算法都需要 8 8 8   步才能确定顺序,但最优解只要 7 7 7   步:
首先任意挑 4 4 4   个数字分两组比较,设比较完后的 5 5 5   个数字是 b 1 < a 1 , b 2 < a 2 , b 3 b_1<a_1,b_2<a_2,b_3 b 1  < a 1  , b 2  < a 2  , b 3   
比较 a 1 a_1 a 1    和 a 2 a_2 a 2    的大小,不妨设 a 1 < a 2 a_1<a_2 a 1  < a 2   。 
将 b 3 b_3 b 3    插入 b 1 < a 1 < a 2 b_1<a_1<a_2 b 1  < a 1  < a 2    这条链里,只需要 2 2 2   步。 
将 b 2 b_2 b 2    插入 ( b 1 , a 1 , b 3 ) (b_1,a_1,b_3) ( b 1  , a 1  , b 3  )   这条链里。因为我们知道 b 2 < a 2 b_2<a_2 b 2  < a 2   ,所以也只需要 2 2 2   步。 
 
Merge-Insertion-Sort  从这个例子受到启发进行递归设计的,具体操作如下:
首先将数字分成 ⌊ N / 2 ⌋ \lfloor N/2 \rfloor ⌊ N /2 ⌋   个组两两比较。如果是奇数那就多出一个数字。设比较完的序列是 b i < a i ( i ∈ 1.. N ) b_i<a_i(i \in 1..N) b i  < a i  ( i ∈ 1.. N )   ,以及可能存在单个 b N / 2 + 1 b_{N/2+1} b N /2 + 1   
对于 a a a   这 N / 2 N/2 N /2   个数调用一次小规模的 Merge-Insertion-Sort 使其有序。 
 
依次把 b b b   序列插入到主链上。具体地,首先分别花 2 2 2   步把 b 3 , b 2 b_3,b_2 b 3  , b 2    (通过二分)插入到 ( b 1 , a 1 , a 2 ) (b_1,a_1,a_2) ( b 1  , a 1  , a 2  )   的链上;然后花 3 3 3   步把 b 5 , b 4 b_5,b_4 b 5  , b 4    插入到链上;然后花 4 4 4   步把 b 11 ∼ b 6 b_{11}\sim b_6 b 11  ∼ b 6    插入到链上…… 
 
现在我们来分析一下 Merge-Insertion-Sort 的具体步数。
设 F ( N ) F(N) F ( N )   是 N N N   个数的比较次数,可知 F ( N ) = ⌊ N / 2 ⌋ + F ( ⌊ N / 2 ⌋ ) + G ( ⌊ N / 2 ⌋ ) F(N)=\lfloor N/2 \rfloor+F(\lfloor N/2 \rfloor)+G(\lfloor N/2 \rfloor) F ( N ) = ⌊ N /2 ⌋ + F (⌊ N /2 ⌋) + G (⌊ N /2 ⌋)  ,其中 G ( N ) G(N) G ( N )   表示将 N N N   个数按上述顺序插入链中的方案数。 
下面我们来计算一下 G ( N ) G(N) G ( N )  。在插链这步,如果把相同步数的分到同一组,那么这个序列是 ( 0 , 2 , 2 , 6 , 10 , …   ) (0,2,2,6,10,\dots) ( 0 , 2 , 2 , 6 , 10 , … )  ,前缀和数列 ( 1 , 1 , 3 , 5 , 11 , …   ) (1,1,3,5,11,\dots) ( 1 , 1 , 3 , 5 , 11 , … )   满足 t k − 1 + t k = 2 k t_{k-1}+t_k=2^k t k − 1  + t k  = 2 k  。 
若 t k − 1 ≤ m ≤ t k , G ( m ) = ( t 1 − t 0 ) + 2 ( t 2 − t 1 ) ⋯ + k ( m − t k − 1 ) = k m − ∑ t i t_{k-1} \le m \le t_k,G(m)=(t_1-t_0)+2(t_2-t_1) \dots +k(m-t_{k-1})=km-\sum t_i t k − 1  ≤ m ≤ t k  , G ( m ) = ( t 1  − t 0  ) + 2 ( t 2  − t 1  ) ⋯ + k ( m − t k − 1  ) = km − ∑ t i   
注意到 ∑ i = 0 m − 1 t i = ⌊ 2 k + 1 / 3 ⌋ \sum \limits_{i=0}^{m-1} t_i=\lfloor 2^{k+1}/3 \rfloor i = 0 ∑ m − 1  t i  = ⌊ 2 k + 1 /3 ⌋  
最后我们可以得到 F ( N ) = ∑ k = 1 N ⌈ log  2 ( 3 k / 4 ) ⌉ F(N)=\sum \limits_{k=1}^N \lceil \log_2(3k/4) \rceil F ( N ) = k = 1 ∑ N  ⌈ log  2  ( 3 k /4 )⌉  
 
我们再来考虑一下这个问题的 Lower_bound。总共有 N ! N! N !   种可能的序列,通过决策树(Decision Tree )定理我们得到下界是 ⌊ log  2 N ! ⌋ \lfloor \log_2{N!} \rfloor ⌊ log  2  N ! ⌋  。
下面我们来感受一下 Binary Comparison Sort,Merge-Insertion-Sort 和 Lower_bound 的效果:
算法 
4 
5 
6 
7 
8 
9 
10 
11 
12 
 
 
LB 
5 
7 
10 
13 
16 
19 
22 
26 
29 
 
MI 
5 
7 
10 
13 
16 
19 
22 
26 
30 
 
BC 
5 
8 
11 
14 
17 
21 
25 
29 
33 
 
 
Adversary Argument 
除了 Decision Tree 外,我们有一个强有力的工具 Adversary 来分析一个算法的下界 。它的核心思想是:Release the least information and change the scenario to make the algorithm work hard.
先来考虑一个 Find Second Maximum Element  问题。我们都知道,找最大值至少需要也仅需要 N − 1 N-1 N − 1   次比较;如果我们重复这个过程,在剩下数里面再找一个最大值,得到该问题的比较次数上界:2 N − 3 2N-3 2 N − 3 
那么如何分析出下界呢?首先我们观察一个很有趣的结论:次大值只能在那些与最大值比较过的数字里产生 。所以该问题的下界其实是 N − 1 + M − 1 N-1+M-1 N − 1 + M − 1  ,其中 M M M   是曾经与最大值比较过的数字。假设回答的策略已经确定,那么最终对下界分析产生影响的 M M M   其实是该策略面对所有算法后取得的最小值,即 M m i n = min  a ∈ A M a M_{min}=\min\limits_{a \in A}M_a M min  = a ∈ A min  M a   。为了分析出原问题 更紧  的下界,我们要找到一种策略,使得它的 M m i n M_{min} M min    尽可能大 。
现在我们要借助 Adversary 分析出尽量大的 M m i n M_{min} M min    。考虑这样一种策略:
给每一个数字 x x x   一个“战胜指数” g ( x ) g(x) g ( x )  ,初始时 g ( x ) = 1 g(x)=1 g ( x ) = 1  。 
假设某一次询问的数对是 ( x , y ) (x,y) ( x , y )   ,Adversary 采用的回答是:若 g ( x ) ≥ g ( y ) g(x) \ge g(y) g ( x ) ≥ g ( y )  ,那么回答 x > y x>y x > y  ,且执行 g ( x ) = g ( x ) + g ( y ) , g ( y ) = 0 g(x) = g(x)+g(y),g(y)=0 g ( x ) = g ( x ) + g ( y ) , g ( y ) = 0  ,反之亦然(这个策略有点像并查集的按秩合并)。 
 
注意到,无论你用什么算法来给出 ( x , y ) (x,y) ( x , y )  ,每次比完一组后,g ( x ) g(x) g ( x )   都不可能超过原来的 2 2 2   倍,而最终时候最大值 z z z   必然满足 g ( z ) = N g(z)=N g ( z ) = N  ,所以 z z z   至少经过了 ⌈ log  N ⌉ \lceil \log N \rceil ⌈ log  N ⌉   次比较后才能确认。即该策略的 M m i n = ⌈ log  N ⌉ M_{min}=\lceil \log N \rceil M min  = ⌈ log  N ⌉  。顺着之前的思路,要确定次大值,必须把这些曾经输掉的数字之间再比试一次。所以得 Find Second Maximum Element 问题的一个比较次数下界是 N + ⌈ log  N ⌉ − 2 N+\lceil \log N \rceil-2 N + ⌈ log  N ⌉ − 2  。
再来考虑一个 Median  问题:有 N N N   个互不相同的数,最少比较几次可以知道这些数里的中位数?
显然排序问题的比较次数是其上界。那么下界是什么样的呢?想象一棵 N N N   个点的树,树边表示我们已知的大小关系。我们幻想的最优情况是:median 这个点成功地把其他点区分开了。即对于一个小于它的邻居,它子树下面也是递减关系;对于一个大于它的邻居,它子树下面也是递增关系。
称 ( x , y ) (x,y) ( x , y )   为 Crucial Comparison ,如果它满足 x > y ≥ m e d i a n x>y \ge median x > y ≥ m e d ian  ,或者 x < y ≤ m e d i a n x<y\le median x < y ≤ m e d ian   。反之就是 Non-Crucial Comparison 。可以发现,如果去对应那 N N N   个点的树,CC 是树边而 NCC 是非树边。
Adversary 提供这样一种策略:使每次询问得到的结果尽量都是 Non-Crucial Comparison。具体地,设状态 N N N   表示还未分配,S S S   表示这个数已小于 Median,L L L   表示这个数已经大于 Median。对于询问的 ( x , y ) (x,y) ( x , y )  :
如果状态是 ( N , N ) (N,N) ( N , N )  ,分配 ( S , L ) (S,L) ( S , L )  
如果状态是 ( N , L ) (N,L) ( N , L )   或者 ( N , S ) (N,S) ( N , S )  ,将 N N N   分配成余下那种状态。 
重复这样的分配方案,直到 ( N − 1 ) / 2 (N-1)/2 ( N − 1 ) /2   个 L L L   或者 S S S   被分配出去了为止。如果按这种方式分配,最终得到的树里至少有 ( N − 1 ) / 2 (N-1)/2 ( N − 1 ) /2   条非树边。再加上 N − 1 N-1 N − 1   条树边,得 Median 问题的比较次数下界是 3 ( N − 1 ) / 2 3(N-1)/2 3 ( N − 1 ) /2   
 
来看 Median 问题里 N = 5 N=5 N = 5   的例子。之前说过,排序的下界是 7 7 7   次,但 Adversary 提供更紧的 6 6 6   次的下界。事实上,只需 6 6 6   次比较的算法确实能构造出来:
先花 3 3 3   步决策出 ( a < b < d , c < d ) (a<b<d,c<d) ( a < b < d , c < d )  。 
此时我们知道 d d d   必然比中位数大,丢掉 d d d  。 
把 e e e   和 c c c   拿去比较,再把大者和 b b b   去比较。花了 2 2 2   步可得 a < b < e , c < e a<b<e,c<e a < b < e , c < e  。 
最后花 1 1 1   步比较 b b b   和 c c c  ,大者就是中位数。 
 
我们再来设计一种 普适的算法  求解 Median 问题,使得比较次数尽量少。一般化问题,设 S e l e c t ( S , k ) Select(S,k) S e l ec t ( S , k )   表示集合 S S S   里第 k k k   小的数,同时设 W ( N ) W(N) W ( N )   表示 ∣ S ∣ = N |S|=N ∣ S ∣ = N   时的  S e l e c t ( S , k ) Select(S,k) S e l ec t ( S , k )   比较次数。
将 S S S   按每组 5 5 5   个划分,分别求中位数。这一步比较次数是 6 ( N / 5 ) 6(N/5) 6 ( N /5 )   的。 
递归调用 W W W   求这 N / 5 N/5 N /5   个中位数的中位数 m ∗ m^\ast m ∗  ,比较次数 W ( N / 5 ) W(N/5) W ( N /5 )  。 
以 m ∗ m^\ast m ∗   为关键字将 S S S   划成两部分 S 1 , S 2 S_1,S_2 S 1  , S 2   。如果 m ∗ m^\ast m ∗   不是中位数:
如果 k ≤ ∣ S 1 ∣ k \le |S_1| k ≤ ∣ S 1  ∣   就调用 S e l e c t ( S 1 , k ) Select(S_1,k) S e l ec t ( S 1  , k )  
否则调用 S e l e c t ( S 2 , k − ∣ S 1 ∣ − 1 ) Select(S_2,k-|S_1|-1) S e l ec t ( S 2  , k − ∣ S 1  ∣ − 1 )  
 
 
任何情况下 ∣ S 1 ∣ , ∣ S 2 ∣ ≤ 7 N / 10 |S_1|,|S_2| \le 7N/10 ∣ S 1  ∣ , ∣ S 2  ∣ ≤ 7 N /10  ,所以 W ( N ) = 6 N / 5 + W ( N / 5 ) + 2 N / 5 + W ( 7 N / 10 ) W(N)=6N/5+W(N/5)+2N/5+W(7N/10) W ( N ) = 6 N /5 + W ( N /5 ) + 2 N /5 + W ( 7 N /10 )  
 
可以用数学归纳法证明 W ( N ) ≤ 16 N W(N) \le 16N W ( N ) ≤ 16 N  。也就是说,Median 问题的比较次数上界是 16 N 16N 16 N  。
再来看一个 Find Max and Min  问题。 一个很优秀的分治做法是:
设 f ( S ) f(S) f ( S )   表示 S S S   集合的最大值和最小值。 
如果 ∣ S ∣ = 2 |S|=2 ∣ S ∣ = 2  ,只要比较一次就能返回结果。 
若 ∣ S ∣ > 2 |S| > 2 ∣ S ∣ > 2  ,将其划分为 S 1 S_1 S 1    和 S 2 S_2 S 2    两个集合,分别求 f ( S 1 ) f(S_1) f ( S 1  )   和 f ( S 2 ) f(S_2) f ( S 2  )  ,把两者的最大值求一个 max  \max max  ,把两者的最小值求一个 min  \min min  。所以步数 T ( N ) = 2 T ( N / 2 ) + 2 T(N)=2T(N/2)+2 T ( N ) = 2 T ( N /2 ) + 2  。 
 
最后解得 T ( N ) = ⌈ 3 2 N ⌉ − 2 T(N)=\lceil \frac{3}{2}N \rceil-2 T ( N ) = ⌈ 2 3  N ⌉ − 2  。现在我们用 Adversary 证明这是下界。
定义四种状态 ( N , W i n , L o s s , B o t h ) \mathrm{(N,Win,Loss,Both)} ( N , Win , Loss , Both )  。其中所有数字都会经过 N \mathrm{N} N   到 W / L \mathrm{W/L} W/L   的转变,还有 N − 2 N-2 N − 2   个数字会经过从 W / L \mathrm{W/L} W/L   到 B \mathrm{B} B   的转变,总信息量是 2 N − 2 2N-2 2 N − 2  。我们希望询问者知道的信息量尽量得少,具体决策如图:
如果比较 ( N , N ) \mathrm{(N,N)} ( N , N )  ,被迫让其获得 2 2 2   信息量变成变成 ( W , L ) \mathrm{(W,L)} ( W , L )  
如果比较 ( N , W ) \mathrm{(N,W)} ( N , W )  ,持续让 W \mathrm{W} W   赢,获得 1 1 1   信息量变成 ( L , W ) \mathrm{(L,W)} ( L , W )  
如果比较 ( W , L / B ) \mathrm{(W,L/B)} ( W , L/B )  ,保持,让其徒劳无获。 
如果比较 ( W , W ) \mathrm{(W,W)} ( W , W )  ,被迫让其获得 1 1 1   信息量变成 ( W , B ) \mathrm{(W,B)} ( W , B )  
 
至多有 N / 2 N/2 N /2   组比较带来了 2 2 2   的信息量。剩下 N − 2 N-2 N − 2   的信息量每个至少消耗一次比较。所以不管以怎样的策略询问,比较次数都不会少于 ⌈ 3 2 N ⌉ − 2 \lceil \frac{3}{2}N \rceil-2 ⌈ 2 3  N ⌉ − 2  ,即这就是该问题的下界。
再看一个 Find x in Matrix   问题:有一个 N × N N \times N N × N   的数字矩阵,矩阵里的数字都互不相同,且每行从左到右和每列从上到下都严格递增。用尽量少的比较次数找到 x x x  ,每次返回 > , < , = >,<,= > , < , =  。
考虑这样一个算法:从右上角开始询问,假设当前的数字是 e e e  。若 e < x e < x e < x   就向下走,若 e > x e > x e > x   就向左走,这样总能找到 x x x  。可以发现,最坏情况下要走 2 N − 1 2N-1 2 N − 1   次。
现在用 Adversary 证明这就是下界。
1 2 3 4 5 6 8 2 3 4 5 6 8 6 3 4 5 6 8 6 5 4 5 6 8 6 5 4 5 6 8 6 5 4 3 6 8 6 5 4 3 2 8 6 5 4 3 2 1 \begin{matrix}   1 & 2 & 3 & 4 & 5 & 6 & 8 \\  2 & 3 & 4 & 5 & 6 & 8 & 6 \\  3 & 4 & 5 & 6 & 8 & 6 & 5 \\  4 & 5 & 6 & 8 & 6 & 5 & 4 \\ 5 & 6 & 8 & 6 & 5 & 4 & 3 \\ 6 & 8 & 6 & 5 & 4 & 3 & 2 \\ 8 & 6 & 5 & 4 & 3 & 2 & 1\end{matrix}
 1 2 3 4 5 6 8  2 3 4 5 6 8 6  3 4 5 6 8 6 5  4 5 6 8 6 5 4  5 6 8 6 5 4 3  6 8 6 5 4 3 2  8 6 5 4 3 2 1  
上述矩阵给出了一个形式化构造(以 N = 7 N=7 N = 7   举例)。任何一个 6 6 6   或者 8 8 8   都有可能被替换成 7 7 7   ,所以 x x x   必须和这些数字都比较一遍。那么如果是 N × N N \times N N × N   的,得出下界是 2 N − 1 2N-1 2 N − 1  。
再来看一个 Merge Sorted Sequence  问题。将两个长度为 N N N   的有序序列合并成一个有序序列,最少需要几次比较?直觉告诉我们,归并排序的 2 N − 1 2N-1 2 N − 1   次比较最少。下面用 Adversary 来证明这一点。
构造数列 X , Y X,Y X , Y   使得 Y i < X j Y_i<X_j Y i  < X j    当且仅当 i < j i<j i < j  (比如 X i = 2 i − 1 , Y i = 2 i X_i=2i-1,Y_i=2i X i  = 2 i − 1 , Y i  = 2 i  )。最终合并完后的序列是 X 1 , Y 1 , X 2 , … , X n , Y n X_1,Y_1,X_2,\dots,X_n,Y_n X 1  , Y 1  , X 2  , … , X n  , Y n   ,假设存在一个算法能在 2 N − 2 2N-2 2 N − 2   步(或更少)完成,那么至少存在一个 i i i   满足:
如果 i ≥ 2 i \ge 2 i ≥ 2  ,X i X_i X i    没有和 Y i Y_i Y i    和 Y i + 1 Y_{i+1} Y i + 1    比较 
如果 i = 1 i=1 i = 1  ,则 X 1 X_1 X 1    没有和 Y 1 Y_1 Y 1    比较。 
 
现在我们把 X i X_i X i    和 Y i Y_i Y i    交换,依然符合题目条件,但是该算法失败了。所以 2 N − 1 2N-1 2 N − 1   次是比较下界。
Unbound Searching 
假设有这样一个定义在正整数域上的函数 F ( x ) F(x) F ( x )   :
F ( x ) = { 0 0 < x < N 1 x ≥ N F(x)=\left\{ \begin{aligned} & 0 \quad &0<x<N \\ & 1 \quad &x \ge N \end{aligned} \right. 
 F ( x ) = {  0 1  0 < x < N x ≥ N  
如何用最少的步数来确定 N N N   的大小?
最先想到的肯定是二分,但是这个问题里 N N N   可能无限趋向于 + ∞ +\infty + ∞  。于是提出了下面这个算法:
定义 Binary search  B 1 B_1 B 1   :
从小到大询问 F ( 2 i − 1 ) F(2^i-1) F ( 2 i − 1 )  (即 F ( 1 ) , F ( 3 ) , F ( 7 ) , … F(1),F(3),F(7),\dots F ( 1 ) , F ( 3 ) , F ( 7 ) , …  )直到 F ( 2 m − 1 ) = 1 F(2^m-1)=1 F ( 2 m − 1 ) = 1  
现在我们知道 N ≤ 2 m − 1 N \le 2^m-1 N ≤ 2 m − 1  ,即 m = ⌊ log  2 N ⌋ + 1 m = \lfloor \log_2N \rfloor + 1 m = ⌊ log  2  N ⌋ + 1  
在 [ 2 m − 1 , 2 m − 1 ) [2^{m-1},2^m-1) [ 2 m − 1 , 2 m − 1 )   上进行二分查找,需要 ⌊ log  2 N ⌋ \lfloor \log_2N \rfloor ⌊ log  2  N ⌋   步。 
总步数 S B 1 = 2 ⌊ log  2 N ⌋ + 1 S_{B_1}=2\lfloor \log_2N \rfloor + 1 S B 1   = 2 ⌊ log  2  N ⌋ + 1  
 
可不可以做得更好呢?我们可以用迭代的方法更快地算出 m m m  !
定义 Double Binary search  B 2 B_2 B 2   :
套用 B 1 B_1 B 1    去求出 m m m  ,需要 2 ⌊ log  2 m ⌋ + 1 2 \lfloor \log_2m \rfloor+1 2 ⌊ log  2  m ⌋ + 1   步。 
重复 B 1 B_1 B 1    的步骤 2 ∼ 3 2\sim3 2 ∼ 3  ,总步数 S B 2 = ⌊ log  2 N ⌋ + 2 ⌊ log  2 ( ⌊ log  2 N ⌋ + 1 ) ⌋ + 1 S_{B_2}=\lfloor \log_2N \rfloor + 2 \lfloor \log_2(\lfloor \log_2N \rfloor+1) \rfloor + 1 S B 2   = ⌊ log  2  N ⌋ + 2 ⌊ log  2  (⌊ log  2  N ⌋ + 1 )⌋ + 1  
 
那么对于 k-Binary Search  B k B_k B k   :S B k ( N ) = L 1 ( N ) + L 2 ( N ) + ⋯ + L k − 1 ( N ) + 2 L k ( N ) + 1 S_{B_k}(N) = L^1(N) + L^2 (N) + \dots + L^{k-1}(N) + 2L^k (N) +1 S B k   ( N ) = L 1 ( N ) + L 2 ( N ) + ⋯ + L k − 1 ( N ) + 2 L k ( N ) + 1 
现在还有一个问题:如何找出我们想要的 k k k   ?设 g ( n ) g(n) g ( n )   满足:
g ( n ) = { 2 n = 1 2 g ( n − 1 ) − 1 n ≥ 2 g(n)=\left\{ \begin{aligned} & 2 \quad &n=1 \\ & 2^{g(n-1)}-1 \quad &n \ge 2 \end{aligned} \right. 
 g ( n ) = {  2 2 g ( n − 1 ) − 1  n = 1 n ≥ 2  
我们从小到大尝试 g g g   数列直到 F ( g ( k ) ) = 1 F(g(k))=1 F ( g ( k )) = 1 
Reduction 
Reduction 是个经典的话题。来看一个经典的 Integer Distinctness Problem 。
给出一个数的集合 { x 1 , x 2 , … , x n } \{x_1,x_2,\dots,x_n\} { x 1  , x 2  , … , x n  }   ,要用尽量少的比较次数,判断其中是否有重复的元素。
显然可以通过 sorting 解决这个问题。神奇的是,我们可以反过来证明 sorting  ∝ \propto ∝    IDP 。
假设 x x x   已经从小到大排好序了,一个结论是:任何解决了 IDP 的算法必然比较过所有 ( x i , x i + 1 ) (x_i,x_{i+1}) ( x i  , x i + 1  )   pair。证明很简单:如果 x j x_j x j    和 x j + 1 x_{j+1} x j + 1    没有比较过,将改成 x j x_j x j    改成 x j + 1 x_{j+1} x j + 1    。算法输出不变,但是结果和之前不同了。 
现在我们开始规约。本来手里有一个 sorting 问题,我们先求解这个序列的 IDP 问题。 
求解 IDP 过程中涉及了若干次比较。把这些比较的结果都保留下来,建成一个有向拓扑图,最后对其跑线性的拓扑排序。由结论 1 1 1  ,所有相邻的 pair 必然出现在图中,所以必然能恢复完整的 sorting 结果。 
所以我们用了和求解 IDP 一样的复杂度,把 sorting 规约到了 IDP。 
 
再来看一个连续规约的例子:3-SUM ∝ \propto ∝   Collinearity ∝ \propto ∝   Segment Splitting 。
3-SUM 问题:有 N N N   个不同的数,是否能选出其中的三个数  ( a , b , c ) (a,b,c) ( a , b , c )   使得 a + b + c = 0 a+b+c=0 a + b + c = 0  
Collinearity 问题:平面上有 N N N   个点,是否能选出三个共线的点。 
Segment Splitting 问题:平面上有 N N N   条线段,是否存在一条直线能将不触碰地将他们划成非空两部分。 
 
3-SUM ∝ \propto ∝   Collinearity 有两种证明方法。一是把原来 3-SUM 里的每个点 a a a   对应到平面上 ( a , a 3 ) (a,a^3) ( a , a 3 )   这个点。 如果 a + b + c = 0 a+b+c=0 a + b + c = 0  ,充要于 ( b 3 − a 3 , b − a ) × ( c 3 − a 3 , c − a ) = 0 (b^3-a^3,b-a) \times (c^3-a^3,c-a)=0 ( b 3 − a 3 , b − a ) × ( c 3 − a 3 , c − a ) = 0  ,反之亦然。二是把原来 3-SUM 里的每个点 a a a   对应到平面上的 ( a , 0 ) , ( a , 2 ) , ( − a 2 , 1 ) (a,0),(a,2),(-\frac{a}{2},1) ( a , 0 ) , ( a , 2 ) , ( − 2 a  , 1 )   三个点。如果 a + b + c = 0 a+b+c=0 a + b + c = 0  ,则 ( a , 2 ) , ( − b 2 , 1 ) , ( c , 0 ) (a,2),(-\frac{b}{2},1),(c,0) ( a , 2 ) , ( − 2 b  , 1 ) , ( c , 0 )   共线,反之亦然。注意这两个方法都要用到斜率,所以都有 ( a , b , c ) (a,b,c) ( a , b , c )   三个数互不相同的前提。
Collinearity ∝ \propto ∝   Segment Splitting 可以沿用上面的证明二。我们在 y = 0 , 1 , 2 y=0,1,2 y = 0 , 1 , 2   上画三条线,对于每个 a a a  ,在对应三个点处“开一个小洞”。这样如果 Collinearity 有解,我们就得到了穿过所有小洞的直线,反之亦然。
因为 3-SUM 是很多问题简化版本,对其的研究突破至关重要。可惜的是,人们已经证明:对于任何一个 ϵ > 0 \epsilon > 0 ϵ > 0  , 不存在 O ( N 2 − ϵ ) O(N^{2-\epsilon}) O ( N 2 − ϵ )   的 3-SUM 解法。
3-SUM 还有一个变种问题 3-SUM’:选数时可以重复选,即 a , b , c a,b,c a , b , c   可以相同。看上去变得更难了?实际上它比 3-SUM 更简单,因为我们可以轻松构造出  3-SUM’  ∝ \propto ∝   3-SUM :对于 3-SUM’ 里的每个数  x x x   ,映射 3-SUM 里的 { a , a − 3 m a x , a + 3 m a x } \{a,a-3max,a+3max\} { a , a − 3 ma x , a + 3 ma x }   三个数(其中 m a x max ma x   是原数集里最大的数字)。
再来看一个厉害的 Reduction:矩阵乘法和矩阵求逆是可以相互规约的 。这里只证明前者规约后者。对于矩阵乘法 A × B A \times B A × B  ,我们可以花 O ( N 2 ) O(N^2) O ( N 2 )   的时间构造出以下 3 N × 3 N 3N \times 3N 3 N × 3 N   的矩阵。注意矩阵求逆后包含 A B AB A B   这一项。
D = ( I n A 0 0 I n B 0 0 I n ) D − 1 = ( I n − A A B 0 I n − B 0 0 I n ) D=
\begin{pmatrix}
I_n & A & 0 \\
0 & I_n & B \\
0 & 0 & I_n
\end{pmatrix}
\quad 
D^{-1}=
\begin{pmatrix}
I_n & -A & AB \\
0 & I_n & -B \\
0 & 0 & I_n
\end{pmatrix}
 D =  I n  0 0  A I n  0  0 B I n    D − 1 =  I n  0 0  − A I n  0  A B − B I n    
Flow and Match 
Ford-Fulkerson  算法是最朴素的最大流算法:每次任意找一条增广路进行增广。值得注意的是,该算法可能永远也不会停止,下图就是一个例子。r 2 = 1 − r , r = 5 – 1 2 ≃ 0.618 r^2 = 1 - r, r=\frac{\sqrt 5 – 1}{2}\simeq 0.618 r 2 = 1 − r , r = 2 5  –1  ≃ 0.618  。更糟糕的是,该算法甚至没有收敛到最大流,因为 1 + 2 ( r 1 + r 2 + r 3 + … ) = 3 + 2 r < 5 1 + 2(r^1 + r^2+ r^3 + …)=3+2r<5 1 + 2 ( r 1 + r 2 + r 3 + … ) = 3 + 2 r < 5 
Edmonds-Karp Algorithm  算法改进了上述过程。它先用 BFS 对点进行标号,每次选择最短的增广路。一条增广路上至少存在一条边 e = ( u , v ) e=(u,v) e = ( u , v )   满足它的流量等于本次新增的流量,我们称之为 critical edge(其实就是增广路上残余流量最少的那些边)。critical edge 会使边完全反向(不存在原方向的流量了),当它下一次又成为 critical edge 时,增广路的长度至少增加 1 1 1  。每条边最多只会成为 critical edge N N N   次,一共有 M M M   条边,每次增广复杂度是 O ( M ) O(M) O ( M )  ,所以总复杂度是 O ( N M 2 ) O(NM^2) O ( N M 2 ) 
我们熟知的 Dinic  算法其实是 EK 算法的多路增广版本。Dinic 引入了分层机制——每次把到终点距离相同的最短路径同时增广,使得复杂度降成 O ( N 2 M ) O(N^2M) O ( N 2 M )  ,而实际速度也很快。下面我们来证明一下:把 Dinic 应用到匹配问题上最多只有 N \sqrt N N    个 Stage。
我们用 V i V_i V i    表示从起点开始顺着流走 i i i   步能到的点的集合。 
假设最终的最大流是 f f f  ,我们有 ∣ V i ∣ ≥ f |V_i| \ge f ∣ V i  ∣ ≥ f  (想要流这么多,每一层至少需要这么多点)。 
对以上不等式求和。假设起点到终点的距离是 D D D  ,则 D ≤ ∣ V ∣ / f D \le |V|/f D ≤ ∣ V ∣/ f  。 
如果 f ≤ ∣ V ∣ f \le \sqrt {|V|} f ≤ ∣ V ∣    就已经证完了,因为每个阶段至少增加 1 1 1   的流量。 
如果 f > ∣ V ∣ f > \sqrt{|V|} f > ∣ V ∣   ,根据以上性质,D ≤ ∣ V ∣ D \le \sqrt{|V|} D ≤ ∣ V ∣   。每轮增广完后下次距离至少加 1 1 1  ,所以最多加 ∣ V ∣ \sqrt{|V|} ∣ V ∣    次。 
 
知乎里  讨论了如何构造出让 Dinic 跑 Θ ( N 2 M ) \Theta(N^2M) Θ ( N 2 M )   的图。
说起匹配问题,还不得不提到一个 霍尔定理 。对于左右各 N N N   个点的匹配问题来说:
∀ P ∈ L ∣ R i g h t ( P ) ∣ ≥ ∣ P ∣ ⇔ |Match(L,R)|=|L|=|R| \forall_{P \in L}|Right(P)| \ge |P| \Leftrightarrow \text{|Match(L,R)|=|L|=|R|}
 ∀ P ∈ L  ∣ R i g h t ( P ) ∣ ≥ ∣ P ∣ ⇔ |Match(L,R)|=|L|=|R| 
右推左是显然。左推右可以反证:考虑网络流跑完的残量网络,取 P P P   为 S S S   割里所有左侧点 。
Computational Geometry 
凸包这个话题想必大家都不陌生。
通过构造点 ( x i , x i 2 ) (x_i,x_i^2) ( x i  , x i 2  )  ,我们可以把排序问题规约到求凸包,也就证明了其下界是 O ( N log  N ) O(N\log N) O ( N log  N )  。
因为凸包的计算几何里的应用很广,如何构建凸包是一个脍炙人口的话题。在算法竞赛里,我们一般用 sort +扫两遍分别求上下凸壳的方法,速度快代码短。下面再来介绍几种有趣的构建方法:
Jarvis March - gift wrapping :从下面的点 s s s   开始,每次枚举一个点 t t t    使得 ( s , t ) (s,t) ( s , t )   这条线段在所有点的右侧。把 t t t   加入集合并重复这一过程。这个算法很是直观啊,复杂度就是 O ( N h ) O(Nh) O ( N h )  。 
我们同样熟悉的 Graham Scan  其实就是对上述算法的优化,用极角排序把复杂度降为 O ( N log  N ) O(N \log N) O ( N log  N )  。 
Quickhull :初始时找到 x x x   最小和最大的两个点连上一条线 ( p , q ) (p,q) ( p , q )  。线的两侧分别做一遍,每次找到一个距离线最远的点 r r r  ,连上 ( p , r ) (p,r) ( p , r )   和 ( q , r ) (q,r) ( q , r )   并重复这个过程。平均复杂度 O ( N log  N ) O(N\log N) O ( N log  N )  ,最坏复杂度 O ( N 2 ) O(N^2) O ( N 2 )  。 
分治算法 :每次按 x x x   大小关系分成两半,各自求凸包并把凸包合并。复杂度 O ( N log  N ) O(N \log N) O ( N log  N )  。 
 
下面我们再来看一个 Line Segment Intersection  问题:给出平面上的 N N N   条线段,求它们两两的交点(如果有的话)。直接暴力是 O ( N 2 ) O(N^2) O ( N 2 )   的,但是如果交点很少,我们就想要找一个更快的做法。
维护一条垂直于 X 轴的 sweeping line (扫描线)。从左往右扫,保证扫描线左边的交点已经全找到了。这里有一个小结论:最近相交的两条线段,它们一定会与扫描线都有交点,而且这两个交点是 相邻  的。所以我们用平衡树按顺序维护所有与扫描线产生交点的线段。我们再用一个堆维护一些关键节点,包括 一条线段的终点位置 和 两条在扫描线上相邻线段的交点。每次弹出最近的关键节点(把扫描线移到那里),更新扫描线上(平衡树里)的点,同时也更新堆里的点。总复杂度是 O ( ( N + K ) log  N ) O((N+K)\log N) O (( N + K ) log  N )  ,K K K   是交点个数。
Voronoi Diagram 
V 图是分析和处理平面图的一个有力的工具,而且他的定义也十分自然。
所谓 V图 ,就是把平面(无限或者有限)分割成若干个部分。初始时有 N N N   个互不相同的点 s k s_k s k   ,每一个区域 C k C_k C k    就是由所有到 s k s_k s k    最近的点构成。这个定义比较拗口,我们来看个直观的例子。
结合左图的例子我们还能发现,每一段区域的边界都是一组 ( s i , s j ) (s_i,s_j) ( s i  , s j  )   的垂直平分线。如果我们在这些 s s s   之间连上线,就可以得到右图这个形状。没错,它就是外围 s k s_k s k    构成的凸包的三角剖分!
定理:若 N ≥ 3 N \ge 3 N ≥ 3  ,V 图最多有 2 N − 5 2N-5 2 N − 5   个顶点和 3 N − 6 3N-6 3 N − 6   条边。
证明:假设有 v v v   个顶点和 e e e   条边。
欧拉定理:( v + 1 ) − e + N = 2 (v+1)-e+N=2 ( v + 1 ) − e + N = 2  
注意一个顶点至少和三条边关联,所以 3 ( v + 1 ) ≤ 2 e 3(v+1) \le 2e 3 ( v + 1 ) ≤ 2 e  
由以上两个式子即可推出 v ≤ 2 N − 5 v \le 2N-5 v ≤ 2 N − 5   且 e ≤ 3 N − 6 e \le 3N-6 e ≤ 3 N − 6  
 
知道了 V 图的点数和边数都是 O ( N ) O(N) O ( N )   的,我们就可以放心地用 V 图来做各种分析了。
O ( N ) O(N) O ( N )   找到 N N N   个点里最近的 Pair:直接枚举 V 图三角剖分里的那些边。 
O ( N ) O(N) O ( N )   求 N N N   个点的凸包:把外围的无限区域里的点顺次连接起来即可。 
 
所以问题的关键是:我们如何高效地建出 N N N   个点 s i s_i s i    对应的 V 图?还是用我们的万金油:分治算法。每次合并都是线性的,所以 V 图可以 O ( N log  N ) O(N \log N) O ( N log  N )   建出来。具体合并细节有点麻烦。
下面我们来看一个经典的 Art Gallery Problems :(凹)多边形内最少布置多少守卫才能看到所有位置。
注意这个问题已被证明是 NP-Hard 的,所以我们尝试求一些紧的界。首先一个显然的上界是 N − 2 N-2 N − 2  ,因为我们可以在三角剖分后每个三角形的中心放一个守卫。至于为什么 N N N   个点的(凹)多边形能剖分成 N − 2 N-2 N − 2   个三角形呢?可以用归纳法证明:
N = 3 N=3 N = 3   时是 1 1 1   个。 
N > 3 N>3 N > 3   时,设 u u u   是最左边的点,v , w v,w v , w   是它相邻的点。如果连接 ( v , w ) (v,w) ( v , w )   后不与别的边相交,就把三角形 ( u , v , w ) (u,v,w) ( u , v , w )   切下来归纳。否则我们在相交侧的点里找一个与 u u u   最近的点 u ′ u' u ′  ,用 ( u , u ′ ) (u,u') ( u , u ′ )   把多边形切割成两半,每一半调用结论归纳。 
 
现在我们再来证明一个更紧的上界。如果把多边形(任意一个)三角形剖分后的边也连上去,我们可以用对偶证明,新图的顶点是可以三染色的。更厉害的是,如果我们在三染色的任意一种颜色处放置守卫,均能覆盖整个多边形!那么挑一种出现次数最少的颜色,我们可以得到 Art Gallery Problems 的上界:⌊ N 3 ⌋ \lfloor \frac{N}{3} \rfloor ⌊ 3 N  ⌋  。
事实上我们还能构造出达到这一上界的多边形,见右图。