2,同理由k a y( 3 , 5) = 4得知M35 由M3 4×M55 算出。依此类推,M34 由M3 3×M44 得出。因而此最优乘法算法的步骤为:
M11×M2 2 = M1 2
M3 3×M4 4 = M3 4
M3 4×M5 5 = M3 5
M1 2×M3 5 = M1 5
计算c(i, j) 和k a y (i, j) 的递归代码见程序1 5 - 6。在函数C中,r 为全局一维数组变量, k a y是全局二维数组变量,函数C返回c(i j) 之值且置k a y [a] [b] =k ay (a , b) (对于任何a , b),其中c(a , b)在计算c(i,j) 时皆已算出。函数Traceback 利用函数C中已算出的k a y值来推导出最优乘法算法的步骤。
设t (q)为函数C的复杂性,其中q=j-i+ 1(即Mij 是q个矩阵运算的结果)。当q为1或2时,t(q) =d,其中d 为一常数;而q> 2时,t (q)=2q-1?k = 1t (k ) +e q,其中e 是一个常量。因此当q>2时,t(q)>2t (q- 1 ) +e,所以t (q)= W ( 2q)。函数Traceback 的复杂性为(q)。
程序15-6 递归计算c (i, j) 和kay (i, j)
int C(int i, int j)
{ / /返回c(i,j) 且计算k(i,j) = kay[i][j]
if (i==j) return 0; //一个矩阵的情形
if (i == j-1) { //两个矩阵的情形
kay[i][i+1] = i;
return r[i]*r[i+1]*r[r+2];}
/ /多于两个矩阵的情形
/ /设u为k = i 时的最小值
int u = C(i,i) + C(i+1,j) + r[i]*r[i+1]*r[j+1];
kay[i][j] = i;
/ /计算其余的最小值并更新u
for (int k = i+1; k < j; k++) {
int t = C(i,k) + C(k+1,j) + r[i]*r[k+1]*r[j+1];
if (r < u) {//小于最小值的情形
u = t;
kay[i][j] = k;
}
return u;
}
void Traceback (int i, int j ,int **kay)
{ / /输出计算Mi j 的最优方法
if ( i == j) return;
Traceback(i, kay[i][j], kay);
Traceback(kay[i][j]+1, j, kay);
cout << "Multiply M" << i << ", "<< kay[i][j];
cout << " and M " << (kay[i][j]+1) << ", " << j << end1;
}
2. 无重复计算的递归方法
若避免再次计算前面已经计算过的c(及相应的k a y),可将复杂性降低到(q3)。而为了避免重复计算,需用一个全局数组c[ ][ ]存储c(i, j) 值,该数组初始值为0。函数C的新代码见程序1 5 - 7:
程序15-7 无重复计算的c (i, j) 计算方法
int C(int i,int j)
{ / /返回c(i,j) 并计算k a y ( i , j ) = k a y [ I ] [ j ]
/ /避免重复计算
/ /检查是否已计算过
if (c[i][j] >) return c[i][j];
/ /若未计算,则进行计算
if(i==j) return 0; //一个矩阵的情形
i f ( i = = j - 1 ) { / /两个矩阵的情形
kay[i][i+1]=i;
c [ i ] [ j ] = r [ i ] * r [ i + 1 ] * r [ i + 2 ] ;
return c[i][j];}
/ /多于两个矩阵的情形
/ /设u为k = i 时的最小值
int u=C(i,i)+C(i+1,j)+r[i]*r[i+1]*r[j+1];
k a y [ i ] [ j ] = i ;
/ /计算其余的最小值并更新u
for (int k==i+1; k<j;k++){
int t=C(i,k)+C(k+1,j)+r[i]*r[k+1]*r[j+1];
if (t<u) {// 比最小值还小
u = t ;
k a y [ i ] [ j ] = k ; }
}
c [ i ] [ j ] = u ;
return u;
}
为分析改进后函数C 的复杂性,再次使用分期计算方法。注意到调用C(1, q) 时每个c (i, j)(1≤i≤j≤q)仅被计算一次。要计算尚未计算过的c(a,b),需附加的工作量s =j-i>1。将s 计入第一次计算c (a, b) 时的工作量中。在依次计算c(a, b) 时,这个s 会转计到每个c (a, b) 的第一次计算时间c 中,因此每个c (i, i) 均被计入s。对于每个s,有q-s+ 1个c(i, j) 需要计算,因此总的工作消耗为q-1 ?s=1(q-s+ 1) = (q3 )。
3. 迭代方法
c 的动态规划递归式可用迭代的方法来求解。若按s = 2,3,.,q-1 的顺序计算c (i, i+s),每个c 和kay 仅需计算一次。
例3-14 考察例3 - 1 3中五个矩阵的情况。先初始化c (i, i) (0≤i≤5) 为0,然后对于i=1, ., 4分别计算c (i, i+ 1 )。c (1, 2)= r1 r2 r3 = 5 0,c (2, 3)= 5 0,c ( 3,4)=20 和c (4, 5) = 2 0 0。相应的k ay 值分别为1,2,3和4。
当s= 2时,可得:
c( 1 , 3 ) = m i n {c( 1 , 1 ) +c(2,3)+ r1 r2 r4 , c( 1 , 2 ) +c( 3 ,3 )+r1r3r4 }=min
=150
且k a y( 1 , 3 ) = 2。用相同方法可求得c( 2 , 4 )和c( 3 , 5 )分别为3 0和4 0,相应k a y值分别为2和3。
当s= 3时,需计算c(1,4) 和c( 2 , 5 )。计算c(2,5) 所需要的所有中间值均已知(见( 1 5 - 5 )式),代入计算公式后可得c( 2 , 5 ) = 9 0,k a y( 2 , 5 ) = 2。c( 1 , 4 )可用同样的公式计算。最后,当s= 4时,可直接用(1 5 - 4)式来计算c( 1 , 5 ),因为该式右边所有项都已知。
计算c 和kay 的迭代程序见函数M a t r i x C h a i n(见程序1 5 - 8),该函数的复杂性为(q3 )。计算出kay 后同样可用程序1 5 - 6中的Traceback 函数推算出相应的最优乘法计算过程。
程序15-8 c 和kay 的迭代计算
void MatrixChain(int r[], int q, int **c, int **kay)
{// 为所有的Mij 计算耗费和k a y
// 初始化c[i][i], c[i][i+1]和k a y [ i ] [ i + 1 ]
for (int i = 1; i < q; i++) {
c[i][i] = 0;
c[i][i+1] = r[i]*r[i+1]*r[i+2];
kay[i][i+1] = i;
}
c[q][q] = 0;
/ /计算余下的c和k a y
for (int s = 2; s < q; s++)
for (int i = 1; i <= q - s; i++) {
// k = i时的最小项
c[i][i+s] = c[i][i] + c[i+1][i+s] + r[i]*r[i+1]*r[i+s+1];
kay[i][i+s] = i;
// 余下的最小项
for (int k = i+1; k < i + s; k++) {
int t = c[i][k] + c[k+1][i+s] + r[i]*r[k+1]*r[i+s+1];
if (t < c[i][i+s]) {// 更小的最小项
c[i][i+s] = t;
kay[i][i+s] = k;}
}
}
}
3.2.4 最短路径
假设G为有向图,其中每条边都有一个长度(或耗费),图中每条有向路径的长度等于该路径中各边的长度之和。对于每对顶点(i, j),在顶点i 与j 之间可能有多条路径,各路径的长度可能各不相同。我们定义从i 到j 的所有路径中,具有最小长度的路径为从i 到j 的最短路径。
例3-15 如图1 5 - 4所示。从顶点1到顶点3的路径有
1) 1,2,5,3
2) 1,4,3
3) 1,2,5,8,6,3
4) 1,4,6,3
由该图可知,各路径相应的长度为1 0、2 8、9、2 7,因而路径3) 是该图中顶点1到顶点3的最短路径。
在所有点对最短路径问题( a l l - p a i r sshorest-paths problem)中,要寻找有向图G中每对顶点之间的最短路径。也就是说,对于每对顶点(i, j),需要寻找从i到j 的最短路径及从j 到i 的最短路径。因此对于一个n 个顶点的图来说,需寻找p =n(n-1) 条最短路径。假定图G中不含有长度为负数的环路,只有在这种假设下才可保证G中每对顶点(i, j) 之间总有一条不含环路的最短路径。当有向图中存在长度小于0的环路时,可能得到长度为-∞的更短路径,因为包含该环路的最短路径往往可无限多次地加上此负长度的环路。
设图G中n 个顶点的编号为1到n。令c (i, j, k)表示从i 到j 的最短路径的长度,其中k 表示该路径中的最大顶点。因此,如果G中包含边<i, j>,则c(i, j, 0) =边<i, j> 的长度;若i= j ,则c(i,j, 0)=0;如果G中不包含边<i, j>,则c (i, j, 0)= +∞。c(i, j, n) 则是从i 到j 的最短路径的长度。
例3-16 考察图1 5 - 4。若k=0, 1, 2, 3,则c (1, 3, k)= ∞;c (1, 3, 4)= 2 8;若k = 5, 6, 7,则c (1, 3,k) = 1 0;若k=8, 9, 10,则c (1, 3, k) = 9。因此1到3的最短路径长度为9。对于任意k>0,如何确定c (i, j, k) 呢?中间顶点不超过k 的i 到j 的最短路径有两种可能:该路径含或不含中间顶点k。若不含,则该路径长度应为c(i, j, k- 1 ),否则长度为c(i, k, k- 1) +c (k, j, k- 1 )。c(i, j, k) 可取两者中的最小值。因此可得到如下递归式:
c( i, j, k)= m i n {c(i, j, k-1), c (i, k, k- 1) +c (k, j, k- 1 ) },k>0
以上的递归公式将一个k 级运算转化为多个k-1 级运算,而多个k-1 级运算应比一个k 级运算简单。如果用递归方法求解上式,则计算最终结果的复杂性将无法估量。令t (k) 为递归求解c (i, j, k) 的时间。根据递归式可以看出t(k) = 2t(k- 1 ) +c。利用替代方法可得t(n) = ( 2n )。因此得到所有c (i, j, n) 的时间为(n2 2n )。
当注意到某些c (i, j, k-1) 值可能被使用多次时,可以更高效地求解c (i, j, n)。利用避免重复计算c(i, j, k) 的方法,可将计算c 值的时间减少到(n3 )。这可通过递归方式(见程序1 5 - 7矩阵链问题)或迭代方式来实现。出迭代算法的伪代码如图1 5 - 5所示。
/ /寻找最短路径的长度
/ /初始化c(i,j,1)
for (int i=1; i < = n ; i + +)
for (int j=1; j<=n; j+ + )
c ( i ,j, 0 ) = a ( i ,j); // a 是长度邻接矩阵
/ /计算c ( i ,j, k ) ( 0 < k < = n )
for(int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
for (int j= 1 ;j< = n ;j+ + )
if (c(i,k,k-1)+c(k,j, k - 1 ) < c ( i ,j, k - 1 ) )
c ( i ,j, k ) = c ( i , k , k - 1 ) + c ( k ,j, k - 1 ) ;
else c(i,j, k ) = c ( i ,j, k - 1 ) ;
图15-5 最短路径算法的伪代码
注意到对于任意i,c(i,k,k) =c(i,k,k- 1 )且c(k,i,k) =c(k,i,k- 1 ),因而,若用c(i,j)代替图1 5 - 5的c(i,j,k),最后所得的c(i,j) 之值将等于c(i,j,n) 值。此时图1 5 - 5可改写成程序1 5 - 9的C + +代码。程序1 5 - 9中还利用了程序1 2 - 1中定义的AdjacencyWDigraph 类。函数AllPairs 在c 中返回最短路径的长度。若i 到j 无通路,则c [i] [j]被赋值为N o E d g e。函数AllPairs 同时计算了k a y [ i ] [ j ],其中kay[i][j] 表示从i 到j 的最短路径中最大的k 值。在后面将看到如何根据kay 值来推断出从一个顶点到另一顶点的最短路径(见程序1 5 - 1 0中的函数O u t p u t P a t h)。
程序1 5 - 9的时间复杂性为(n3 ),其中输出一条最短路径的实际时间为O (n)。
程序15-9 c 和kay 的计算
template<class T>
void AdjacencyWDigraph<T>::Allpairs(T **c, int **kay)
{ / /所有点对的最短路径
/ /对于所有i和j,计算c [ i ] [ j ]和k a y [ i ] [ j ]
/ /初始化c [ i ] [ j ] = c(i,j,0)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
c[i][j] = a[i][j];
kay[i][j] = 0;
}
for (i = 1; i <= n; i++)
c[i][i] = 0;
// 计算c[i][j] = c(i,j,k)
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
T t1 = c[i][k];
T t2 = c[k][j];
T t3 = c[i][j];
if (t1 != NoEdge && t2 != NoEdge && (t3 == NoEdge || t1 + t2 < t3)) {
c[i][j] = t1 + t2;
kay[i][j] = k;}
}
}
程序15-10 输出最短路径
void outputPath(int **kay, int i, int j)
{// 输出i 到j 的路径的实际代码
if (i == j) return;
if (kay[i][j] == 0) cout << j << ' ';
else {outputPath(kay, i, kay[i][j]);
o u t p u t P a t h ( k a y, kay[i][j], j);}
}
template<class T>
void OutputPath(T **c, int **kay, T NoEdge, int i, int j)
{// 输出从i 到j的最短路径
if (c[i][j] == NoEdge) {
cout << "There is no path from " << i << " to " << j << endl;
r e t u r n ; }
cout << "The path is" << endl;
cout << i << ' ';
o u t p u t P a t h ( k a y, i , j ) ;
cout << endl;
}
例3-17 图15-6a 给出某图的长度矩阵a,15-6b 给出由程序1 5 - 9所计算出的c 矩阵,15-6c 为对应的k a y值。根据15-6c 中的kay 值,可知从1到5的最短路径是从1到k a y [ 1 ] [ 5 ] = 4的最短路径再加上从4到5的最短路径,因为k a y [ 4 ] [ 5 ] = 0,所以从4到5的最短路径无中间顶点。从1到4的最短路径经过k a y [ 1 ] [ 4 ] = 3。重复以上过程,最后可得1到5的最短路径为:1,2,3,4,5。
3.2.5 网络的无交叉子集
在11 . 5 . 3节的交叉分布问题中,给定一个每边带n 个针脚的布线通道和一个排列C。顶部的针脚i 与底部的针脚Ci 相连,其中1≤i≤n,数对(i, Ci ) 称为网组。总共有n 个网组需连接或连通。假设有两个或更多的布线层,其中有一个为优先层,在优先层中可以使用更细的连线,其电阻也可能比其他层要小得多。布线时应尽可能在优先层中布设更多的网组。而剩下的其他网组将布设在其他层。当且仅当两个网组之间不交叉时,它们可布设在同一层。我们的任务是寻找一个最大无交叉子集(Maximum Noncrossing Su b s e t,M N S )。在该集中,任意两个网组都不交叉。因(i, Ci ) 完全由i 决定,因此可用i 来指定(i, Ci )。
例3-18 考察图1 5 - 7(对应于图1 0 - 1 7)。( 1 , 8 )和( 2 , 7 )(也即1号网组和2号网组)交叉,因而不能布设在同一层中。而( 1 , 8 ),(7,9) 和(9,10) 未交叉,因此可布设在同一层。但这3个网组并不能构成一个M N S,因为还有更大的不交叉子集。图1 0 - 1 7中给出的例子中,集合{ ( 4 , 2 ) ,( 5 , 5 ) , ( 7 , 9 ) , ( 9 , 1 0 )}是一个含4个网组的M N S。
设M N S(i, j) 代表一个M N S,其中所有的(u, Cu ) 满足u≤i,Cu≤j。令s i z e(i,j) 表示M N S(i,j)的大小(即网组的数目)。显然M N S(n,n)是对应于给定输入的M N S,而s i z e(n,n)是它的大小。
例3-19 对于图1 0 - 1 7中的例子,M N S( 1 0 , 1 0 )是我们要找的最终结果。如例3 - 1 8中所指出的,s i z e( 1 0 , 1 0 ) = 4,因为( 1 , 8 ),( 2 , 7 ),( 7 , 9 ),( 8 , 3 ),( 9 , 1 0 )和( 1 0 , 6 )中要么顶部针脚编号比7大,要么底部针脚编号比6大,因此它们都不属于M N S( 7 , 6 )。因此只需考察剩下的4个网组是否属于M N S( 7 , 6 ),如图1 5 - 8所示。子集{( 3 , 4 ) , ( 5 , 5 )}是大小为2的无交叉子集。没有大小为3的无交叉子集,因此s i z e( 7 , 6) = 2。
当i= 1时,( 1 ,C1) 是M N S( 1 ,j) 的唯一候选。仅当j≥C1 时,这个网组才会是M N S( 1 ,j) 的一个成员.
下一步,考虑i>1时的情况。若j<Ci,则(i,Ci ) 不可能是M N S( i,j) 的成员,所有属于M N S(i,j) 的(u, Cu ) 都需满足u<i且Cu<j,因此:s i z e(i,j) =s i z e(i- 1 ,j), j<Ci (1 5 - 7)
若j≥Ci,则(i,Ci ) 可能在也可能不在M N S(i,j) 内。若(i,Ci ) 在M N S(i,j) 内,则在M N S(i,j)中不会有这样的(u,Cu ):u<i且Cu>Ci,因为这个网组必与(i, Ci ) 相交。因此M N S(i,j) 中的其他所有成员都必须满足条件u<i且Cu<Ci。在M N S(i,j) 中这样的网组共有Mi- 1 , Ci- 1 个。若(i,Ci ) 不在M N S(i,j)中,则M N S(i,j) 中的所有(u, Cu ) 必须满足u<i;因此s i z e(i,j)=s i z e(i- 1 ,j)。虽然不能确定(i, Ci )是否在M N S(i,j) 中,但我们可以根据获取更大M N S的原则来作出选择。因此:s i z e(i,j) = m a x {s i z e(i-1 ,j), s i z e(i- 1 ,Ci-1)+1}, j≥Ci (1 5 - 8)
虽然从(1 5 - 6)式到( 1 5 - 8)式可用递归法求解,但从前面的例子可以看出,即使避免了重复计算,动态规划递归算法的效率也不够高,因此只考虑迭代方法。在迭代过程中先用式(1 5 - 6)计算出s i ze ( 1 ,j),然后再用式(1 5 - 7)和(1 5 - 8)按i=2, 3, ., n 的顺序计算s i ze (i,j),最后再用Traceback 来得到M N S(n, n) 中的所有网组。
例3-20 图1 5 - 9给出了图1 5 - 7对应的s i z e(i,j) 值。因s i z e( 1 0 , 1 0) = 4,可知M N S含4个网组。为求得这4个网组,先从s i ze ( 1 0 , 1 0 )入手。可用(1 5 - 8)式算出s i z e( 1 0 , 1 0 )。根据式(1 5 - 8)时的产生原因可知s i ze ( 1 0 , 1 0)=s i z e( 9 , 1 0 ),因此现在要求M NS ( 9 , 1 0 )。由于M NS ( 1 0 , 1 0 )≠s i z e( 8 , 1 0 ),因此M NS (9,10) 中必包含9号网组。M N S(9,10) 中剩下的网组组成M NS ( 8 , C9- 1)=M N S( 8 , 9 )。由M N S( 8 , 9 ) =M NS (7,9) 知,8号网组可以被排除。接下来要求M N S( 7 , 9 ),因为s i z e( 7 , 9 )≠s i z e( 6 , 9 ),所以M N S中必含7号网组。M NS (7,9) 中余下的网组组成M NS ( 6 , C7- 1 ) =M N S( 6 , 8 )。根据s i z e( 6 , 8 ) =s i z e( 5 , 8 )可排除6号网组。按同样的方法, 5号网组,3号网组加入M N S中,而4号网组等其他网组被排除。因此回溯过程所得到的大小为4的M N S为{ 3 , 5 , 7 , 9 }。
注意到在回溯过程中未用到s i z e( 1 0 ,j) (j≠1 0 ),因此不必计算这些值。
程序1 5 - 11给出了计算s i z e ( i , j ) 的迭代代码和输出M N S的代码。函数M N S用来计算s i ze (i,j) 的值,计算结果用一个二维数组M N来存储。size[i][j] 表示s i z e(i,j),其中i=j= n 或1≤i<n,0≤j≤n,计算过程的时间复杂性为(n2 )。函数Traceback 在N et[0 : m - 1]中输出所得到的M N S,其时间复杂性为(n)。因此求解M M S问题的动态规划算法的总的时间复杂性
为(n2 )。
程序1 5 - 11 寻找最大无交叉子集
void MNS(int C[], int n, int **size)
{ / /对于所有的i 和j,计算s i z e [ i ] [ j ]
/ /初始化s i z e [ 1 ] [ * ]
for (int j = 0; j < C[1]; j++)
size[1][j] = 0;
for (j = C[1]; j <= n; j++)
size[1][j] = 1;
// 计算size[i][*], 1 < i < n
for (int i = 2; i < n; i++) {
for (int j = 0; j < C[i]; j++)
size[i][j] = size[i-1][j];
for (j = C[i]; j <= n; j++)
size[i][j] = max(size[i-1][j], size[i-1][C[i]-1]+1);
}
size[n][n] = max(size[n-1][n], size[n-1][C[n]-1]+1);
}
void Traceback(int C[], int **size, int n, int Net[], int& m)
{// 在N e t [ 0 : m - 1 ]中返回M M S
int j = n; // 所允许的底部最大针脚编号
m = 0; // 网组的游标
for (int i = n; i > 1; i--)
// i 号n e t在M N S中?
if (size[i][j] != size[i-1][j]){// 在M N S中
Net[m++] = i;
j = C[i] - 1;}
// 1号网组在M N S中?
if (j >= C[1])
Net[m++] = 1; // 在M N S中
}
3.2.6 元件折叠
在设计电路的过程中,工程师们会采取多种不同的设计风格。其中的两种为位-片设计(bit-slice design)和标准单元设计(standard-cell design)。在前一种方法中,电路首先被设计为一个元件栈(如图15-10a 所示)。每个元件Ci 宽为wi ,高为hi ,而元件宽度用片数来表示。图15-10a 给出了一个四片的设计。线路是按片来连接各元件的,即连线可能连接元件Ci 的第j片到元件Ci+1 的第j 片。如果某些元件的宽度不足j 片,则这些元件之间不存在片j 的连线。当图1 5 -10a 的位-片设计作为某一大系统的一部分时,则在V L SI ( Very Large Scale Integrated) 芯片上为它分配一定数量的空间单元。分配是按空间宽度或高度的限制来完成的。现在的问题便是如何将元件栈折叠到分配空间中去,以便尽量减小未受限制的尺度(如,若高度限制为H时,必须折叠栈以尽量减小宽度W)。由于其他尺度不变,因此缩小一个尺度(如W)等价于缩小面积。可用折线方式来折叠元件栈,在每一折叠点,元件旋转1 8 0°。在图15-10b 的例子中,一个1 2元件的栈折叠成四个垂直栈,折叠点为C6 , C9 和C1 0。折叠栈的宽度是宽度最大的元件所需的片数。在图15-10b 中,栈宽各为4,3,2和4。折叠栈的高度等于各栈所有元件高度之和的最大值。在图15-10b 中栈1的元件高度之和最大,该栈的高度决定了包围所有栈的矩形高度。
实际上,在元件折叠问题中,还需考虑连接两个栈的线路所需的附加空间。如,在图1 5 -10b 中C5 和C6 间的线路因C6 为折叠点而弯曲。这些线路要求在C5 和C6 之下留有垂直空间,以便能从栈1连到栈2。令ri 为Ci 是折叠点时所需的高度。栈1所需的高度为5 ?i =1hi +r6,栈2所需高度为8 ?i=6hi +r6+r9。
在标准单元设计中,电路首先被设计成为具有相同高度的符合线性顺序的元件排列。假设此线性顺序中的元件为 C1,.,Cn,下一步元件被折叠成如图1 5 - 11所示的相同宽度的行。在此图中, 1 2个标准单元折叠成四个等宽行。折叠点是C4,C6 和C11。在相邻标准单元行之间,使用布线通道来连接不同的行。折叠点决定了所需布线通道的高度。设li 表示当Ci 为折叠点时所需的通道高度。在图1 5 - 11的例子中,布线通道1的高度为l4,通道2的高度为l6,通道3的高度为l11。位-片栈折叠和标准单元折叠都会引出一系列的问题,这些问题可用动态规划方法来解决。
1. 等宽位-片元件折叠
定义r1 = rn+1 =0。由元件Ci 至Cj 构成的栈的高度要求为j ?k= ilk+ ri+ rj + 1。设一个位-片设计中所有元件有相同宽度W。首先考察在折叠矩形的高度H给定的情况下,如何缩小其宽度。设Wi
为将元件Ci 到Cn 折叠到高为H的矩形时的最小宽度。若折叠不能实现(如当ri +hi>H时),取Wi =∞。注意到W1 可能是所有n 个元件的最佳折叠宽度。
当折叠Ci 到Cn 时,需要确定折叠点。现假定折叠点是按栈左到栈右的顺序来取定的。若第一点定为Ck+ 1,则Ci 到Ck 在第一个栈中。为了得到最小宽度,从Ck+1 到Cn 的折叠必须用最优化方法,因此又将用到最优原理,可用动态规划方法来解决此问题。当第一个折叠点k+ 1已知时,可得到以下公式:
Wi =w+ Wk + 1 (1 5 - 9)
由于不知道第一个折叠点,因此需要尝试所有可行的折叠点,并选择满足( 1 5 - 9)式的折叠点。令h s u m(i,k)=k ?j = ihj。因k+ 1是一个可行的折叠点,因此h s u m(i, k) +ri +rk+1 一定不会超过H。
根据上述分析,可得到以下动态规划递归式:
这里Wn+1 =0,且在无最优折叠点k+ 1时Wi 为∞。利用递归式(1 5 - 1 0),可通过递归计算Wn , Wn- 1., W2 , W1 来计算Wi。Wi 的计算需要至多检查n-i+ 1个Wk+ 1,耗时为O (n-k)。因此计算所有Wi 的时间为O (n2 )。通过保留式(1 5 - 1 0)每次所得的k 值,可回溯地计算出各个最优的折叠点,其时间耗费为O (n)。
现在来考察另外一个有关等宽元件的折叠问题:折叠后矩形的宽度W已知,需要尽量减小其高度。因每个折叠矩形宽为w,因此折叠后栈的最大数量为s=W / w。令Hi, j 为Ci , ., Cn 折叠成一宽度为jw 的矩形后的最小高度, H1, s 则是所有元件折叠后的最小高度。当j= 1时,不允许任何折叠,因此:Hi,1 =h s u m(i,n) +ri , 1≤i≤n
另外,当i=n 时,仅有一个元件,也不可能折叠,因此:Hn ,j=hn+rn , 1≤j≤s
在其他情况下,都可以进行元件折叠。如果第一个折叠点为k+ 1,则第一个栈的高度为
h s u m(i,k) +ri +rk+ 1。其他元件必须以至多(j- 1 ) *w 的宽度折叠。为保证该折叠的最优性,其他元件也需以最小高度进行折叠.
因为第一个折叠点未知,因此必须尝试所有可能的折叠点,然后从中找出一个使式(1 5 - 11)的右侧取最小值的点,该点成为第一个折叠点。
可用迭代法来求解Hi, j ( 1≤i≤n, 1≤j≤s),求解的顺序为:先计算j=2 时的H i, j,再算j= 3,.,以此类推。对应每个j 的Hi, j 的计算时间为O (n2 ),所以计算所有H i, j 的时间为O(s n2 )。通过保存由( 1 5 - 1 2)式计算出的每个k 值,可以采用复杂性为O (n) 的回溯过程来确定各个最优的折叠点。
2. 变宽位-片元件的折叠
首先考察折叠矩形的高度H已定,欲求最小的折叠宽度的情况。令Wi 如式(1 5 - 1 0)所示,按照与(1 5 - 1 0)式相同的推导过程,可得:
Wi = m i n {w m i n(i, k) +Wk+1 | h s u m(i,k)+ ri +rk+ 1≤H, i≤k≤n} (1 5 - 1 3)
其中Wn+1=0且w m i n(i,k)= m ini≤j≤k{wj }。可用与(1 5 - 1 0)式一样的方法求解(1 5 - 1 3)式,所需时间为O(n2 )。
当折叠宽度W给定时,最小高度折叠可用折半搜索方法对超过O(n2 )个可能值进行搜索来实现,可能的高度值为h(i,j)+ri +rj + 1。在检测每个高度时,也可用( 1 5 - 1 3)式来确定该折叠的宽度是否小于等于W。这种情况下总的时间消耗为O (n2 l o gn)。
3. 标准单元折叠
用wi 定义单元Ci 的宽度。每个单元的高度为h。当标准单元行的宽度W 固定不变时,通过减少折叠高度,可以相应地减少折叠面积。考察Ci 到Cn 的最小高度折叠。设第一个折叠点是Cs+ 1。从元件Cs+1 到Cn 的折叠必须使用最小高度,否则,可使用更小的高度来折叠Cs+1 到Cn,从而得到更小的折叠高度。所以这里仍可使用最优原理和动态规划方法。
令Hi , s 为Ci 到Cn 折叠成宽为W的矩形时的最小高度,其中第一个折叠点为Cs+ 1。令w s u m(i, s)=s ?j = iwj。可假定没有宽度超过W的元件,否则不可能进行折叠。对于Hn,n 因为只有一个元件,不存在连线问题,因此Hn, n =h。对于H i, s(1≤i<s≤n)注意到如果w s u m(i, s )>W,不可能实现折叠。若w s u m(i,s)≤W,元件Ci 和C j + 1 在相同的标准单元行中,该行下方布线通道的高度为ls+ 1(定义ln+1 = 0)。因而:Hi, s = Hi+1, k (1 5 - 1 4)
当i=s<n 时,第一个标准单元行只包含Ci 。该行的高度为h 且该行下方布线通道的高度为li+ 1。因Ci+ 1 到Cn 单元的折叠是最优的.
为了寻找最小高度折叠,首先使用式( 1 5 - 1 4)和(1 5 - 1 5)来确定Hi, s (1≤i≤s≤n)。最小高度折叠的高度为m in{H1 , s}。可以使用回溯过程来确定最小高度折叠中的折叠点。