算法导论26 Problems 答案
26-1
a
使用得到P-MAT-VEC-RECURSIVE
类似的方法对SUM-ARRAYS
进行修改,同样可以得到其基于递归的并行版本SUM-ARRAYS-RECURSIVE
:
1 | SUM-ARRAYS-RECURSIVE(A, B, C, i, i') |
可见这个算法的工作量为\(T_1(n)=\Theta(n)\)。由于其递归深度达到\(\Theta(\lg n)\),并且只需要花费\(\Theta(1)\)的时间即可完成,因此其持续时间为\(T_{\infty}(n)=\Theta(\lg n)\cdot\Theta(1)=\Theta(\lg n)\)。因此的并行度为\(T_1(n)/T_{\infty}(n)=\Theta(n/\lg n)\)。
b
如果\(grain\text{-}size=1\),那么\(r=n\),SUM-ARRAYS'
中的for
循环将会串行地执行\(n\)次,每次调用ADD-SUBARRAY
都只花费\(\Theta(1)\)的时间。因此持续时间\(T_{\infty}(n)=n\cdot
\Theta(1)=\Theta(n)\)。可以知道其工作量为\(T_1(n)=\Theta(n)\),因此其并行度为\(T_1(n)/T_{\infty}(n)=\Theta(1)\)。
c
令\(g=grain\text{-}size\)。执行单次ADD-SUBARRAY
所需要的时间为\(O(g)\)。在SUM-ARRAYS'
中,一共需要执行\(n/g\)次ADD-SUBARRAY
,但是for
循环并没有带有parrllel
关键字,也就是说,这些调用ADD-SUBARRAY
的线程是按顺序启动的,它们并非同时启动的,因此这里仍然需要花费\(O(n/g)\)的时间完成这个for
循环。
因此,SUM-ARRAYS'
需要花费\(O(g+n/g)\)的时间完成。令\(f(g)=g+n/g\),那么有\(f'(g)=1-n/g^2\)。令\(f'(g)=00\),即\(g=\sqrt{n}\)时,SUM-ARRAYS'
只需要花费\(O(\sqrt{n})\)的时间就可以完成。
26-2
a
修改后的代码由P-MATRIX-MULTIPLY-RECURSIVE'
所示,它消去了临时矩阵\(D\)的存在。
1 | P-MATRIX-MULTIPLY-RECURSIVE'(A, B, C, n) |
可见,它的串行投影是MATRIX-MULTIPLY-RECURSIVE
,因此其工作量为\(\Theta(n^3)\)。
b
对于其工作量\(T_1(n)\),可以给出其递推式\(T_1(n)=8T_1(n/2)+\Theta(n^2)\),因此得到\(T_1(n)=\Theta(n^3)\)。
对于其持续时间,除了递归调用,P-MATRIX-MULTIPLY-RECURSIVE'
没有再做其它工作。因此可以给出其递推式\(T_{\infty}(n)=2T_{\infty}(n/2)+\Theta(1)\),最终通过主定理可以得知\(T_{\infty}(n)=\Theta(n)\)。
也就是说,算法P-MATRIX-MULTIPLY-RECURSIVE'
的并行度为\(T_1(n)/T_{\infty}(n)=\Theta(n^2)\)。
c
忽略掉\(\Theta\)符号后,对于\(1000\times1000\)的矩阵而言,调用P-MATRIX-MULTIPLY-RECURSIVE'
的并行度为\(10^6\)。相比于调用P-MATRIX-MULTIPLY-RECURSIVE
得到\(10^7\)的并行度,计算机依旧不会有这么多的处理器,但是在算法的运行时间的常数却明显减少了,因此这种权衡是值得的。
26-3
a
对LU-DECOMPOSITION
修改后得到的并行化版本为P-LU-DECOMPOSITION
:
1 | P-LU-DECOMPOSITION(A, n) |
也就是说,所有内循环都可以进行并行,但是最外层的那一层循环不允许并行。第一个内循环能够并行的原因是:它只读取\(a\)矩阵的元素,并修改\(L,U\)对应行和对应列的元素,并不会进行多次存取。第二个双重内循环能够并行的原因是第\(i\)轮二层循环,第\(j\)轮三次循环中,他只会读取\(L,U\)矩阵的值并且修改\(a_{ij}\)的值,不会造成冲突。外层循环不能是并行的原因是按照LU分解的定义,后一次的运行依赖于前一次的结果。
P-LU-DECOMPOSITION
的串行投影是LU-DECOMPOSITION
,因此其工作量为\(T_1(n)=\Theta(n^3)\)。
对于P-LU-DECOMPOSITION
的持续时间,第一层的第一次内循环花费的时间是\(\Theta(\lg(n-k))=\Theta(\lg
n)\),因为转化成spawn ... sync
结构后就变成了分支法,然后进行递归。第二次的两个双重嵌套寻呼按的原因相同,其花费的时间为\(\Theta(\lg(n-k))+\Theta(\lg(n-k))=\Theta(\lg
n)\)。因此,外层for
循环需要\(\Theta(\lg
n)\)的时间完成一次。最终,P-LU-DECOMPOSITION
的持续时间为\(T_{\infty}(n)=n\cdot \Theta(\lg n)=\Theta(n\lg
n)\)。
最终我们可以计算出P-LU-DECOMPOSITION
的并行度为\(T_1(n)/T_{\infty}(n)=\Theta(n^2/\lg
n)\)。
b
对LUP-DECOMPOSITION
修改后得到的并行化版本为P-LUP-DECOMPOSITION
:
1 | P-LUP-DECOMPOSITION(A, n) |
第2行的for
循环可以并行,这是显而易见的。第6-9行的for
循环是可以并行的,因为这是寻找一个最大值,在这个过程中,\(n\)个元素构成一棵\(n\)个叶节点的二叉树,对于内部节点,它是两个子节点中的最大值,由于这棵树的深度为\(\Theta(\lg
n)\),因此这段循环的持续时间为\(\Theta(\lg
n)\)。第13行的for
循环用于交换两行元素,显而易见是可以并行的。对于第15行和17行的for
循环,第16行仅仅是对\(a_{ik}\)进行更新,此后都是对\(a_{ij}(j>k)\)中的所有元素进行更新。至于先对哪一行操作都是没有关系的,因此第15行和17行的for
循环它们都可以并行。外面针对\(k\)的for
循环不能并行,因为会导致数据读取冲突。
P-LUP-DECOMPOSITION
的串行投影是LUP-DECOMPOSITION
,因此其工作量为\(T_1(n)=\Theta(n^3)\)。
对于P-LUP-DECOMPOSITION
的持续时间,分析和P-LU-DECOMPOSITION
非常相似。第6,13,15,17行的for
循环都可以进行,因此这部分只使用\(\Theta(\lg
n)\)的时间即可完成。最终,P-LUP-DECOMPOSITION
的持续时间为\(T_{\infty}(n)=n\cdot \Theta(\lg n)=\Theta(n\lg
n)\)。
最终我们可以计算出P-LUP-DECOMPOSITION
的并行度为\(T_1(n)/T_{\infty}(n)=\Theta(n^2/\lg
n)\)。
c
对LUP-SOLVE
修改后得到的并行化版本为P-LUP-SOLVE
:
1 | P-LUP-SOLVE(L, U, π, b, n) |
第4和9行的for
循环可以并行,只需要将长度为\(n\)的序列构造出一棵\(n\)个叶子节点的完全二叉树即可,对于内部节点,它是两个子节点中的值之和。因此,这两个内部for
循环都能够在\(\Theta(\lg
n)\)的时间内完成。对于第2和7行的for
循环,因为后面计算\(x,y\)值需要依赖前面已经计算出的\(x,y\)值。
最终,P-LUP-SOLVE
的串行投影即为LUP-SOLVE
,因此其工作量为\(T_1(n)=\Theta(n^2)\)。按照上面的结论,可以得到持续时间\(T_{\infty}(n)=n\cdot \Theta(\lg n)=\Theta(n\lg
n)\)。最终我们可以计算出P-LUP-SOLVE
的并行度为\(T_1(n)/T_{\infty}(n)=\Theta(n/\lg
n)\)。
d
一个对正定矩阵求逆的并行算法由P-MATRIX-INVERSION-POSITIVE-DEFINITE
给出。
1 | P-MATRIX-INVERSION-POSITIVE-DEFINITE(A, n) |
按照第28.2章的结论以及不等式28.15,可以知道其工作量\(I_1(n)\le 2I_1(n/2)+4M_1(n/2)+O(n^2)\),最终得到\(I_1(n)=O(M(n))\)。
在并行版本中,由题目26.1-8,26.2-3和题26-1-a可知,矩阵的转置、加法和乘法都可以在\(\Theta(\lg
n)\)时间内完成。在P-MATRIX-INVERSION-POSITIVE-DEFINITE
中,一共进行了\(2\)次矩阵转置,\(4\)次矩阵加减法,\(4\)次矩阵乘法。同样的,它们还进行了\(2\)次大小为\(n/2\times
n/2\)矩阵的求逆。可见,这些步骤都是依赖于前一步的结果,因此步骤之间不能直接并行。我们可以写出它的持续时间\(I_{\infty}(n)\)满足\(I_{\infty}(n)=2I_{\infty}(n/2)+10\cdot\Theta(\lg
n)\),因此最终得到\(I_{\infty}(n)=\Theta(n)\)。
最终我们可以得到它的并行度为\(I_1(n)/I_{\infty}(n)=O(M(n)/n)\)。
26-4
a
设计的P-REDUCE
如下所示:
1 | P-REDUCE(x, i, j) |
不难发现它的串行投影即为REDUCE
,因此它的工作量为\(T_1(n)=\Theta(n)\)。由于这棵进行搜索的二叉树的深度为\(\Theta(\lg
n)\),因此这个算法的持续时间为\(T_{\infty}(n)=\Theta(\lg n)\)。
b
这个算法相当于是以\(k=1,2,\dots,n\)对P-REDUCE
都进行了一次调用。因此P-SCAN-1
的工作量为\(\displaystyle{T_1(n)=\sum_{k=1}^n\Theta(k)=\Theta(n^2)}\)。
接下来求解P-SCAN-1
的持续时间。由于对P-SCAN-1
对P-REDUCE
的间接调用呈树形,假设伪代码中,第\(i\)次对P-REDUCE
的调用的持续时间为\(iter(i)\),那么有\(T_{\infty}(n)=\Theta(\lg n)+\max\{iter(i):1\le
i\le n\}\),按照题目26-4-a的结论,有\(T_{\infty}(n)=\Theta(\lg n)\)。
因此P-SCAN-1
的并行度为\(T_1(n)/T_{\infty}(n)=\Theta(n^2/\lg
n)\)。
c
我们可以使用归纳法进行证明其正确性。即证明,调用P-SCAN-2-AUX(i, j)
,对于\(\forall k\in[i,j]\),它都正确计算出了\(y[k]=x[i]\otimes x[i+1]\otimes\dots\otimes
x[k]\)。
对于基本情况:\(i=j\),P-SCAN-2-AUX
都正确计算出了\(y\)数组,这由P-SCAN-2-AUX
的前两行明显可知。
当\(i<j\)时,假设对于区间\([i,j]\)内的所有真子区间\([i',j']\),即\(i\le i'\le j'\le j\),且\(i=i',j=j'\)不同时成立,P-SCAN-2-AUX
都计算出了正确的结果。那么第三行得到了一个\(k=\lfloor(i+j)/2\rfloor\)。对于第4和5行的代码,它们分别对\(y\)数组的区间\([i,k]\)和\([k+1,j]\)进行写入,并且读取的内容也不相交,因此这两行代码不会构成竞争。在第6行结束后,\(y\)数组满足:如果\(p\le k\),那么\(y[p]=x[i]\otimes x[i+1]\otimes\dots\otimes
x[p]\),否则\(y[p]=x[k+1]\otimes
x[k+2]\otimes\dots\otimes x[k]\)。对于\(p>k\)的情况,第8行将\(y[p]\)重新赋值成\(y[k]\otimes y[p]\)。这个步骤完成后,\(\forall p\in[i,j]\),都有\(y[p]=x[i]\otimes x[i+1]\otimes\dots\otimes
x[p]\)。因此P-SCAN-2-AUX
是正确的。
接下来考虑P-SCAN-2-AUX
的工作量\(T_1(n)\),消去最后的for
循环中的parallel
关键字后,那么除去递归部分,它的运行时间是\(\Theta(n)\)。因此可以写出\(T_1(n)=2T_1(n/2)+\Theta(n)\),从而得到\(T_1(n)=\Theta(n\lg n)\)。
接下来考虑其持续时间\(T_{\infty}(n)\)。可见出了递归部分,其余部分仍然需要\(\Theta(\lg n)\)进行。因此有\(T_{\infty}(n)=T_{\infty}(n/2)+\Theta(\lg n)\),最终得到\(T_{\infty}(n)=\Theta(\lg^2n)\)。
因此P-SCAN-2
的并行度为\(T_1(n)/T_{\infty}(n)=\Theta(n/\lg
n)\)。
d
P-SCAN-UP
第8行填入的是$right ⊗ t[k]
;P-SCAN-DOWN
的第5行填入v
,第6行填入v ⊗ t[k]
。
需要注意的是,\(t[k]\)存储的是当一个区间\([i,j]\)被尽量均匀地划分成两个区间\([i,k],[k+1,j]\)后,\(t[k]\)就是记录区间\([i,j]\)前面一半元素(即\([i,k]\))的元素之和\(t[k]=x[i]\otimes x[i+1]\otimes\dots\otimes x[k]\)。
首先证明,调用时每个\(t[k]\)最多只会被赋值一次(因此,在执行P-SCAN-DOWN
时不会造成任何冲突)。只有调用P-SCAN-UP
,当\(i<j\)时,才会对\(t[k]\)进行赋值,在此之后更深的递归讲不会再对\(t[k]\)进行赋值,原因如下。如果递归的区间是左半子区间\([i,k]\),那么对于所有\([i,k]\)的长度大于等于\(2\)的(等于\(1\)则不会进入到这个分支)真子区间\([i',k']\),都有\(\lfloor(i'+k')/2\rfloor<k\),因此\(t[k]\)不会被重复赋值;如果递归的是右子区间\([k+1,j]\),那么它的访问和读写只会在这个区间内进行,更不会对\(t[k]\)进行访问。因此,\(t[k]\)确实能够正确记录区间\([i,j]\)的信息。根据P-SCAN-DOWB
的第5行代码,就可以知道\(t[k]\)记录的是区间i,j的左半区间的元素之和。并且,P-SCAN-UP
的返回值是\([i,j]\)这个区间的所有元素之和,因此结论成立。
接下来证明每次调用P-SCAN-DOWN(v, x, t, y, i, j)
时,总满足\(v=x[1]\otimes x[2]\otimes\dots\otimes
x[i-1]\)。同样使用归纳法来证明。在P-SCAN-3
调用P-SCAN-DOWN
时,有\(v=x[1],i=2\),因此基本情况下是成立的。P-SCAN-DOWN
首先调用P-SCAN-DOWN(v, x, t, y, i, k)
,由于参数\(i\)没有变化,因此\(v\)仍然使用原来的\(v\),原结论成立;然后调用P-SCAN-DOWN(v ⊗ t[k], x, t, y, k + 1, j)
,由于此时\(t[k]=x[i]\otimes x[i+1]\otimes\dots\otimes
x[k]\),因此有\(v\otimes
t[k]=x[1]\otimes x[2]\otimes\dots\otimes
x[k]\)。因此第二次调用时同样满足题目的条件。因此当P-SCAN-DOWN
进入第2行后,y[i] = v ⊗ x[i]
则是\(y[i]=x[1]\otimes x[2]\otimes\dots\otimes
x[i]\),\(y[i]\)被正确地计算出来。
因此,算法P-SCAN-3
是正确的。
e
可以发现,这棵树的节点数仍然是\(\Theta(n)\),因此P-SCAN-3
的工作量\(T_1(n)=\Theta(n)\)。
接下来首先考虑P-SCAN-UP
。由于其每次划分出了两个长度尽量均等的区间,并且其余处理部分都只要\(\Theta(1)\)的时间,因此P-SCAN-UP
这段代码的持续时间满足\(T_{\infty}(n)=T_{\infty}(n/2)+\Theta(1)\),从而得到\(T_{\infty}(n)=\Theta(\lg
n)\)。P-SCAN-DOWN
和P-SCAN-UP
的结构基本相同,因此对其分析也一样。
最终,P-SCAN-3
的持续时间为\(T_{\infty}(n)=\Theta(\lg n)\)。
因此,P-SCAN-3
的并行度为\(T_1(n)/T_{\infty}(n)=\Theta(n/\lg
n)\)。
f
修改后的P-SCAN-3
由P-SCAN-3'
给出。代价是并发度将会降低。
1 | P-SCAN-3'(x, n) |
\(\star\) g
1 | P-SCAN-4(x, n) |
这个算法的基本思想是,将\(x\)分成\(\lceil
n/(2l)\rceil\)块,每一块的长度为\(2l\)(最后一块不足\(2l\)也以一块记),并且\(l\)是\(2\)的幂。一开始\(l=1\),将前半块的最后一个元素\(x[i+l-1]\)添加到后半块\(x[i+l+j](0\le
j<l)\)中的每一个元素。从而最终完成后,每一块内部都是对应值的前缀和。可见两个parallel for
循环不会导致冲突,因此这个算法的持续时间为\(T_{\infty}(n)=\Theta(\lg^2n)\)。
h
将字符串中的(
视为\(+1\),')'
视为\(-1\),得到一个数组\(x\),求出其前缀和\(y\)。那么一个括号字符串是合法的,当且仅当\(\forall i\in[1,n],y_i\ge 0\),并且有\(y_n=0\)。通过对P-SCAN-3
进行改造,我们可以得到一个在\(\Theta(\lg
n)\)时间内判断一个括号字符串是否合法的程序PARENTHESES-IS-WELL-FORMED
:
1 | PARENTHESES-IS-WELL-FORMED(s, n) |
改造后的程序还返回了前缀和数组中的最小值。
26-5
a
不失一般性,这里假设矩阵\(A\)的大小\(n\)是\(2\)的幂次。基于等式26.9,那么对矩阵\(A\)的填充并行分治算法由SIMPLE-STENCIL
给出。
1 | SIMPLE-STENCIL(A, n) |
可见这个算法只是为了填充原来矩阵的所有元素,由其串行投影可以知道其工作量满足\(T_1(n)=4T_1(n/2)+\Theta(1)\),可以得到\(T_1(n)=\Theta(n^2)\)。
这个算法的持续时间满足\(T_{\infty}(n)=3T_\infty(n/2)+\Theta(1)\),可以得到\(T_{\infty}(n)=\Theta(n^{\lg 3})\)。
最终可以得到并行度为\(T_1(n)/T_{\infty}(n)=\Theta(n^{2-\lg 3})\)。
b
不失一般性,这里假设矩阵\(A\)的大小\(n\)是\(3\)的幂次,那么按照题目26-5-a的结果,我们可以对SIMPLE-STENCIL
修改成SIMPLE-STENCIL3
。
1 | SIMPLE-STENCIL3(A, n) |
和题目26-3-a分析的过程类似,可以知道其工作量满足\(T_1(n)=9T_1(n/3)+\Theta(1)\),可以得到\(T_1(n)=\Theta(n^2)\)。
这个算法的持续时间满足\(T_{\infty}(n)=5T_\infty(n/3)+\Theta(1)\),可以得到\(T_{\infty}(n)=\Theta(n^{\log_3 5})\)。
最终可以得到并行度为\(T_1(n)/T_{\infty}(n)=\Theta(n^{2-\log_3 5})\)。
c
利用类似的方式修改题目26-5-a和26-5-b的代码,可以给出SIMPLE-STENCIL-B
代码为:
1 | SIMPLE-STENCIL-B(A, n) |
和题目26-3-a和26-3-b分析的过程类似,可以知道其工作量满足\(T_1(n)=b^2T_1(n/b)+\Theta(1)\),可以得到\(T_1(n)=\Theta(n^2)\)。
这个算法的持续时间满足\(T_{\infty}(n)=(2b-1)T_\infty(n/b)+\Theta(1)\),可以得到\(T_{\infty}(n)=\Theta(n^{\log_b (2b-1)})\)。
最终可以得到并行度为\(T_1(n)/T_{\infty}(n)=\Theta(n^{2-\log_b (2b-1)})\)。
为了证明\(T_1(n)/T_{\infty}(n)=\Theta(n^{2-\log_b (2b-1)})=o(n)\),那么需要证明\(f\forall b\ge 2\),都有\(2-\log_b(2b-1)<1\),即证明\(\log_b(2b-1)>1\)。由于\(\forall b\ge 2\),都有\(2b-1>b\),因此\(\log_b(2b-1)>1\)成立是很显然的。因此有\(T_1(n)/T_{\infty}(n)=o(n)\)。
d
这个算法由STENCIL
给出,并且它还能对更一般形式的矩阵进行填充。
1 | STENCIL(A, n) |
可以知道STENCIL
的工作量是\(T_1(n)=\Theta(n^2)\),因为它仅仅是从小到大枚举矩阵的每条副对角线,并按顺序填入一个个元素。
可以知道STENCIL
的持续时间满足\(T_\infty(n)=n\cdot\Theta(\lg n)=\Theta(n\lg
n)\)。因为第\(k\)轮迭代最多也只会进行\(n\)次操作。如果去掉关键字parallel
关键字并转化为spawn ... sync
结构,那么这一部分需要花费\(\Theta(\lg n)\)的时间。
因此,STENCIL
的并行度为\(T_1(n)/T_{\infty}(n)=\Theta(n/\lg
n)\)。
如果需要达到\(\Theta(n)\)的并行度,那么可以将parallel for
循环转化成硬编码形式,
从而去掉这个parallel for
循环,并且一次性在\(\Theta(1)\)的时间内生产\(\Theta(n)\)个元素,从而使并行度提升到\(\Theta(n)\)。但是这里使用了parallel for
循环,它是基于分治法实现的,因此达不到这个最大并行度。
26-6
a
工作量定律将改写成\(E[T_P]\ge E[T_1]/P\);持续时间定律将改写成\(E[T_P]\ge E[T_{\infty}]\);贪心调度界限改写成\(E[T_P]\le E[T_1]/P+E[T_{\infty}]\)。
b
按照题目给出的数据,令\(P=10000\),那么可以写出以下三条式子:
\(\begin{aligned} E[T_1]&=10^4\cdot 0.01+10^9\cdot 0.99\\ E[T_P]&=1\cdot 0.01+10^9\cdot0.99\\ E[T_1/T_P]&=\dfrac{10^4}{1}\cdot0.01+\dfrac{10^9}{10^9}\cdot0.99\\ \end{aligned}\)
从而得到\(E[T_1/T_P]\approx100,\dfrac{E[T_1]}{E[T_P]}\approx 1\)。
选用\(\dfrac{E[T_1]}{E[T_P]}\)的原因有如下两个:
由于在绝大多数时间中,无论是\(P=1\)还是\(P=10000\),其运行时间大多数都在\(10^9\),因此说明这个添加到\(P=10000\)的操作应该对加速作用不大,因此选用\(\dfrac{E[T_1]}{E[T_P]}\)是一个比较恰当的值。
\(T_1/T_P\)这个随机变量并不成立。题目中没有提到\(T_1=10^4\)和\(T_1=10^9\)的时机是否和\(T_P=1\)和\(T_P=10^9\)的时机相同。也就是说,它们不一定是相关的。因此采用独立计算更加合适,即选用\(\dfrac{E[T_1]}{E[T_P]}\)。
c
当\(P\)趋于无穷时,期望的加速比应该和并行度相等。这和题目26-6-b使用的定义是一致的。
d
我们可以对第7.3章提到的RANDOMIZED-QUICKSORT
算法提出其并行化版本P-RANDOMIZED-QUICKSORT
(按照题目要求,不对RANDOMIZED-PARTITION
并行化):
1 | P-RANDOMIZED-QUICKSORT(A, p, r) |
e
可见,P-RANDOMIZED-QUICKSORT
的串行投影为RANDOMIZED-QUICKSORT
,因此其期望工作量\(E[T_1(n)]=O(n\lg n)\)。
对于P-RANDOMIZED-QUICKSORT
持续时间,考虑将P-RANDOMIZED-QUICKSORT
和RANDOMIZED-SELECT
的行为进行对比。P-RANDOMIZED-QUICKSORT
的RANDOMIZED-PARTITION
和RANDOMIZED-SELECT
中的一样,这部分都是占据了主导的时间\(\Theta(n)\)。因此,对其划分的推导也和第9.2章给的完全一样。接下来考虑其递归部分。可以发现,P-RANDOMIZED-QUICKSORT
的阶段划分和RANDOMIZED-SELECT
也相同,并且证明过程同样也考虑了那个执行时间比较长的执行分支。因此对P-RANDOMIZED-QUICKSORT
持续时间\(T_{\infty}(n)\)的分析过程和RANDOMIZED-SELECT
的分析过程完全一致。按照定理9.2的结论,我们得到\(T_{\infty}(n)=\Theta(n)\)。
因此,P-RANDOMIZED-QUICKSORT
的并行度为\(T_1(n)/T_{\infty}(n)=O(\lg n)\)。
f
RANDOMIZED-SELECT
的并行化版本由P-RANDOMIZED-SELECT
给出。其中,给定的P-RANDOMIZED-PARTITION
由题目26.3-3的P-PARTITION
实现而来。
1 | P-RANDOMIZED-PARTITION(A, p, r) |
可见这个算法的串行投影为RANDOMIZED-SELECT
,因此它的工作量为\(T_1(n)=\Theta(n)\)。
令示性遍历\(X_k\)表示P-RANDOMIZED-PARTITION
划分出来后的元素在于位置\(k\)。那么由于程序的其余部分都需要\(\Theta(\lg
n)\)完成(根据题目26.3-3的结论,这里的主要开销就在于P-RANDOMIZED-PARTITION
需要花费\(\Theta(\lg
n)\)的时间),因此可以对随机变量\(T_{\infty}(n)\)可以写出如下递推式:
\[T_{\infty}(n)=\sum_{i=1}^n X_k\cdot T_{\infty}(\max\{k-1,n-k\})+\Theta(\lg n)\]
可见\(E[X_k]=\dfrac{1}{n}\),因为每个位置都有可能被选到。那么对式子左右两侧取期望值,那么有
\(\begin{aligned} E[T_{\infty}(n)]&=\sum_{k=1}^n E[X_k]\cdot E[T_{\infty}(\max\{k-1,n-k\})]+\Theta(\lg n)\\ &\le\dfrac{2}{n}\sum_{k=\lfloor n/2\rfloor}^{n-1} E[T_{\infty}(k)]+\Theta(\lg n)\\ \end{aligned}\)
接下来使用代入法证明\(E[T_{\infty}(n)]=O(n^d)\),即\(\exists c,n_0>0,d\in(0,1)\),使得\(\forall n\ge n_0\),都有\(E[T_{\infty}(n)]\le c\cdot n^{d}\)。那么就可以得到
\(\begin{aligned} E[T_{\infty}(n)]&\le\dfrac{2}{n}\sum_{k=\lfloor n/2\rfloor}^{n-1} E[T_{\infty}(k)]+\Theta(\lg n)\\ &\le\dfrac{2}{n}\sum_{k=\lfloor n/2\rfloor}^{n-1} c\cdot k^d+\Theta(\lg n)\\ &=\dfrac{2c}{n}\sum_{k=\lfloor n/2\rfloor}^{n-1} k^d+\Theta(\lg n)\\ &\le \dfrac{2c}{n}\int_{\lfloor n/2\rfloor}^n x^d dx+\Theta(\lg n)\\ &=\dfrac{2c}{n}\cdot\left.\dfrac{x^{d+1}}{d+1}\right|_{x=\lfloor n/2\rfloor}^n+\Theta(\lg n)\\ &=c\cdot\dfrac{2-2^{-d}}{d+1}\cdot n^d + \Theta(\lg n) \end{aligned}\)
考虑关于\(d\)的一元一次不等式\(\dfrac{2-2^{-d}}{d+1}<1\),可以得到\(d>0\)。
也就是说,无论\(d\)取\((0,1)\)中的什么值,只要第一个项中的\(c\)足够大,它就可以覆盖到\(\Theta(\lg n)\)中的常数,从而最终得到\(T_{\infty}(n)\)
因此,\(T_{\infty}(n)=o(n^d)\),其中\(d\)是任意正数。
最终可以得到并行度为\(T_1(n)/T_{\infty}(n)=\Theta(n^{1-d})\)。