0x05算法设计与分析复习(二):算法设计策略-分治法4

本文介绍分治法的基本概念,并详细探讨了利用分治法解决选择问题的具体算法实现,包括随机主元选择法及线性时间选择算法。此外,还介绍了斯特拉森矩阵乘法算法,这是一种通过减少子矩阵乘法次数来提高效率的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考书籍:算法设计与分析——C++语言描述(第二版)

算法设计策略-分治法

选择问题

问题描述

选择问题(select problem)是指在n个元素的集合中,选出某个元素值大小在集合中处于第k位的元素,即所谓的求第k小元素问题(Kth-smallest)。

分治法求解

如果使用快速排序中所采用的选取主元的分划方法,以主元为基准,将一个表划分为左右两个子表。设原表的长度为n,经过一趟划 分,分成左右两个子表,其中左子表时主元及其左边的元素的集合,长度为p;右子表是主元右边元素的集合。那么,如果k=pk=pk=p,则主元就是第k小元素;否则若k<pk<pk<p,第k小元素就在左子表中,求解的子问题就是在左子表中寻找第k小元素;若k>pk>pk>p,第k小元素就在有子表中,求解的子问题就是在右子表中寻找第k−pk-pkp小元素。

//Select 函数
//随机选主元算法 
//假定表中元素各不相同,并且随机选择主元,即在下标区间[left,right]中随机选择一个下标r,以该下标处的元素为主元。 

template<class T>
  ResaultCode SortableList<T>::Select1(T& x, int k)
  {
    if(n<=0 || k>n || k<=0)
      return OutOfBounds;
    int left = 0, right = n; l[n] = INFTY;//INFITY是一个大值
    do{
      int j = rand()%(right-left+1)+left;//随机选择主元
      Swap(left,j);//将主元交换至位置left处
      j=Partition(left,right);//执行分划操作
      if(k==j+1){//找到
        x=l[j];
        return Success;
      }	else if(k<j+1){
        right = j;
      }	else{
        left = j+1;
      }
    }while(true);
  }

上述算法与快速排序有相同的最坏情况时间复杂度O(n2)O(n^2)O(n2),当其平均时间复杂度是线性的,即O(n)O(n)O(n)

线性时间选择算法

下面是最坏情况下具有线性时间的求第k小元素的算法。

改进的选择算法采用二次取中法(median of medians rule)确定主元。通过精心挑选分划元素,可以使得所得的两个子集合的大小相对接近,从而使得求第k小元素的最坏情况时间具有线性时间O(n)O(n)O(n)

//线性时间选择算法
ResultCode SortableList<T>::Select(T& x, int k)
{
  if(n<=0||k>n||k<=0)
    return OutOfBounds;
  int j=Select(k,0,n-1,5);
  x=l[j];
  return Success;
}
template<class T>
  int SortableList<T>::Select(int k, int left, int right, int r)
  {
    int n = right-left+1;
    //若问题足够小,使用直接插入排序,取其中的第k小元素,其下标为left+k-1
    if(n<=r){
      InsertSort(left, right);
      return left+k-1;
    }
    for(int i = 1;i<n/r; i++){
      //二次取中规则求每一组的中间值
      InsertSort(left+(i-1)*r,left+i*r-1);
      //将每组的中间值集中存放在子表前部
      Swap(left+i+1,left+(i-1)*r+Ceil(r,2)-1);
    }
    int j = Select(Ceil(n/r,2),left,left+(n/r)-1,r);//求二次中间值,其下标为j
    Swap(left,j);//二次中间值为主元,并换至left处
    j=Partition(left,right);//对表进行分划操作
    if(k==j-left+1)
      return j;//返回k小元素下标
    else{
      if(k<j-left+1)
        return Select(k,left,j-1,r);//在左子表中求第k小元素
      else
        return Select(k-(j-left+1),j+1,right,r);//在右子表中求第k-(j-left+1)小元素
    }
  }

斯特拉森矩阵乘法

普通的矩阵乘法算法的时间复杂度为Θ(n3)\Theta(n^3)Θ(n3)

分治法

假定n是2的幂,n=2kn=2^kn=2k。如果相乘的矩阵不是方阵,可以通过增加全零行或列来成为2的幂的方阵。当分治法用于求解矩阵相乘问题时,可以采用对矩阵分块的方法将其分解为4个(n/2)×(n/2)(n/2)\times (n/2)(n/2)×(n/2)的矩阵,
[A11A12A21A22][B11B12B21B22]=[C11C12C21C22]C11=A11B11+A12B21C12=A11B12+A12B22C21=A21B11+A22B21C22=A21B12+A22B22 \begin{bmatrix} A_{11} &A_{12} \\ A_{21} &A_{22} \end{bmatrix} \begin{bmatrix} B_{11} &B_{12} \\ B_{21} &B_{22} \end{bmatrix} =\begin{bmatrix} C_{11} &C_{12} \\ C_{21} &C_{22} \end{bmatrix}\\ C_{11}=A_{11}B_{11}+A_{12}B_{21}\\ C_{12}=A_{11}B_{12}+A_{12}B_{22}\\ C_{21}=A_{21}B_{11}+A_{22}B_{21}\\ C_{22}=A_{21}B_{12}+A_{22}B_{22} [A11A21A12A22][B11B21B12B22]=[C11C21C12C22]C11=A11B11+A12B21C12=A11B12+A12B22C21=A21B11+A22B21C22=A21B12+A22B22

如果子矩阵还不够小,则还需要进一步划分,直到每个子矩阵只含有一个元素,可以直接计算其乘积。

但是这种分治法的时间复杂度仍为O(n3)O(n^3)O(n3)

斯特拉森分治法

斯特拉森通过减少子矩阵的乘法次数来降低时间复杂度。其处理方式是,先执行7次子矩阵的乘法和10次加(减)法得到7个中间子矩阵:
P=(A11+A22)(B11+B22)Q=(A21+A22)B11R=A11(B12−B22)S=A22(B21−B11)T=(A11+A12)B22U=(A21−A11)(B11+B12)V=(A12−A22)(B21+B22) P=(A_{11}+A_{22})(B_{11}+B_{22})\\ Q=(A_{21}+A_{22})B_{11}\\ R=A_{11}(B_{12}-B_{22})\\ S=A_{22}(B_{21}-B_{11})\\ T=(A_{11}+A_{12})B_{22}\\ U=(A_{21}-A_{11})(B_{11}+B_{12})\\ V=(A_{12}-A_{22})(B_{21}+B_{22}) P=(A11+A22)(B11+B22)Q=(A21+A22)B11R=A11(B12B22)S=A22(B21B11)T=(A11+A12)B22U=(A21A11)(B11+B12)V=(A12A22)(B21+B22)

然后再使用8次子矩阵加(减)法得到:
C11=P+S−T+VC12=R+T;C21=Q+S;C22=P+R−Q+U C_{11}=P+S-T+V\\ C_{12}=R+T;\\ C_{21}=Q+S;\\ C_{22}=P+R-Q+U C11=P+ST+VC12=R+T;C21=Q+S;C22=P+RQ+U

于是
T(n)={b n≤27T(n/2)+dn2 n>2 T(n)=\left\{\begin{matrix} b&\ n\leq 2\\ 7T(n/2)+dn^2&\ n>2 \end{matrix}\right. T(n)={b7T(n/2)+dn2 n2 n>2
$T(n)=\Theta(n^{\log7})\approx \Theta(n^{2.81}) $。

在Strassen之后又有许多算法改进了矩阵乘法的计算时间复杂性。目前最好的计算时间上界是
O(n2.376)O(n^{2.376})O(n2.376)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值