<H3>1.2 列表的并行前缀</H3>
<>指针转移技术远不止应用于表排序问题。在算术电路中可以运用“前缀”计算来执行快速二进制加法。现在我们来探讨如何运用指针转移技术来进行前缀计算。有关前缀问题的EREW算法对由n个对象组成的表的运行时间为O(lgn)。</P>
<>前缀计算是根据二进制中满足结合律的运算符<FONT face="MT Symbol">Ä</FONT>来决定的。计算时输入为序列<x<SUB>1</SUB>,x<SUB>2</SUB>,...,x<SUB>n</SUB>>并产生一个输出序列<y<SUB>1</SUB>,y<SUB>2</SUB>,...,y<SUB>n</SUB>>,满足y<SUB>1</SUB>=x<SUB>1</SUB>,并且对k=2,3,...,n,有</P>
<BLOCKQUOTE>
<><IMG src="http://iprai.hust.edu.cn/icl2002/algorithm/advanced/parallel_algorithm/images/Eqn2.gif"></P></BLOCKQUOTE>
<P>换句话说,每个y<SUB>k</SUB>是序列的头k个元素一起“相乘”而得到的。因此才有“前缀”这一意义。</P>
<P>作为前缀计算的一个实例,假定n个对象组成的表中的每个元素包含的值为1,并设<FONT face="MT Symbol">Ä</FONT>表示普通加法运算。因为对k=1,2,...,n,表中第k个元素包含的值为x<SUB>k</SUB>=1。所以前缀计算后的结果为y<SUB>k</SUB>=k,即为第k个元素的下标。因此,进行表排序的另一种方法是先把表颠倒(可以在O(1)的时间内完成),执行前缀计算,然后把计算所得的每个值减1。</P>
<P>我们现在说明EREW算法是如何在O(lgn)的运行时间里对n个对象组成的表进行前缀计算的。为了方便起见,我们定义符号记法:</P>
<BLOCKQUOTE>
<P><IMG src="http://iprai.hust.edu.cn/icl2002/algorithm/advanced/parallel_algorithm/images/Eqn3.gif"></P></BLOCKQUOTE>
<P>其中整数i和j满足1≤i≤j≤n。则对k=1,2,..,n,有[k,k]=x<SUB>k</SUB>,并且对0≤i≤j≤k≤n,</P>
<BLOCKQUOTE>
<P><IMG src="http://iprai.hust.edu.cn/icl2002/algorithm/advanced/parallel_algorithm/images/Eqn4.gif"></P></BLOCKQUOTE>
<P>根据这一符号记法,前缀计算的目标就是计算y<SUB>k</SUB>=[1,k],k=1,2,..n。</P>
<P>当我们对表进行前缀计算时,我们希望输入序列<x<SUB>1</SUB>,x<SUB>2</SUB>,...,x<SUB>n</SUB>>的顺序由对象在链表中的链接关系来确定,而不是由存储对象的存储器阵列中对象的下标来确定。下列EREW算法开始时,表L中每个对象i都有一个相应的值x。如果对象i是从表头开始的第k个对象,则x=x<SUB>k</SUB>为输入序列中的第k个元素。因此,并行前缀计算产生y=y<SUB>k</SUB>=[1,k]。</P><CODE class=pseudocode><PRE>下面的算法使用了伪代码进行描述,请参见<a href="http://iprai.hust.edu.cn/icl2002/algorithm/algorithm/pseudocode.htm" target="_blank" >伪代码的使用</A></PRE><PRE>
List-Prefix(L)</PRE><PRE>1. for 每个处理器i,并行地执行</PRE><PRE>2. do y ← x</PRE><PRE>3. while 存在一个对象i满灶next≠NIL</PRE><PRE>4. do for 每个处理器i,并行地执行</PRE><PRE>5. do if next≠NIL</PRE><PRE>6 then y[next]← y<FONT face="MT Symbol">Ä</FONT>y[next];</PRE><PRE>7 next← next[next];</CODE></PRE>
<P>上述伪代码和图3说明了这个算法和List-Rank的相似之处。仅有的差别在于初始化以及更新值的不同(d或y)。在List-Rank中,处理器i更新其自身的d;在List-Prefix中,处理器i更新的是另一个处理器的y[next]。注意,List-Prefix与List-Rank一样也是EREW算法,因为指针转移技术维持以下条件不变:对不同的对象i和j,或者next≠next[j],或者next=next[j]=NIL。</P>
<P>图3说明了while循环中的每一次迭代执行前表的状态。这一过程保持下列条件不变:在第t次while循环执行结束时,第k个处理器中存放了[max(1,k-2<SUP>t</SUP>+1),k],k=1,2,..,n。在第一次迭代过程中,除最后一个对象的指针为NIL外,表中第k个对象均指向初始时的第k+1个对象。第6行使得第k个对象(k=1,2,..,n-1)从其后继中取得值[k+1,k+1]。然后执行运算[k,k]<FONT face="MT Symbol">Ä</FONT>[k+1,k+1],得到[k,k+1],再把它存储回其后继中。然后,next指针与在List-Rank中一样进行转移,得到图3(b)所示的第一次迭代后的结果。第二次迭代也是类似的。对k=1,2,...,n-2,第k个对象从其后继(由其next域的新的值所定义)取得值[k+1,k+2],然后把[k-1,k]<FONT face="MT Symbol">Ä</FONT>[k+1,k+2]=[k-1,k+2]存入其后继中。结果如图3(c)所示。在第三次也是最后一次迭代中,只有表的开头两个对象的指针不是NIL,它们从其各自的表中分别从其后继取得相应的值。最后的结果如图3(d)所示。我们注意到使算法List-Prefix能够工作的关键是在每一步,如果我们对每一个存在的表进行先缀计算,则每个对象均包含正确的值。</P>
<P align=center><IMG src="http://iprai.hust.edu.cn/icl2002/algorithm/advanced/parallel_algorithm/images/fig3a.gif"></P>
<P align=center>(a)</P>
<P align=center><IMG src="http://iprai.hust.edu.cn/icl2002/algorithm/advanced/parallel_algorithm/images/fig3b.gif"></P>
<P align=center>(b)</P>
<P align=center><IMG src="http://iprai.hust.edu.cn/icl2002/algorithm/advanced/parallel_algorithm/images/fig3c.gif"></P>
<P align=center>(c)</P>
<P align=center><IMG src="http://iprai.hust.edu.cn/icl2002/algorithm/advanced/parallel_algorithm/images/fig3d.gif"></P>
<P align=center>(d)</P>
<P align=center>图3 在链表上并行前缀算法List-Prefix的执行过程</P>
<P>因为上面介绍的两种算法运用了同样的指针转移技术,所以对List-Prefix的分析与List-Rank相同:在EREW PRAM上的运行时间为O(lgn),算法执行的全部工作为O(nlgn)。</P><!-- #EndEditable -->[ |