自学内容网 自学内容网

程序设计方法与实践-分治法

分而治之

  • 一个问题可以分成若干个子问题进行求解(子问题规模最好相同)
  • 对子问题进行求解(可以递归进行)
  • 然后对子问题的解进行合并

所以分治法的通用步骤可以概括为:分、治、合。 

分治法与主定理

这里大家回忆一下,算法效率分析中的主定理法分析递归算法的原则:

T\left ( n \right )=a\left ( T\left [ n/b \right ] \right )+\Theta (n^{2}) 

这不就是分治法的一种体现吗?也就是将规模为T\left ( n \right )的问题,划分成a个规模为T\left [ n/b \right ]的子问题,后面跟着的余项\Theta (n^{2})即为”合“的时间复杂度。 

归并排序

  • 将待排序数组分成两部分:a\left [ 0..n/2 \right ]a\left [ n/2...n-1 \right ]
  • 分别将两部分递归方式进行排序
  • 然后将两部分有序数列合并

伪码描述: 

MergeSort(A[0...n-1]){
    copy A[0...n/2-1] to B[0...n/2-1]
    copy A[n/2...n-1] to C[0...n/2-1]
    MergeSort(B[0...n/2-1])
    MergeSort(C[0...n/2-1])
    Merge(B,C,A)
}

看起来好像什么都没干,但却是解决了这个问题。这就是递归函数的魅力,同样它可能会让你忽视时间的效率。 当然这个算法是比较优的啦:

如果使用主定理法去计算该算法的时间复杂度的话,不难发现其时间复杂度为:\Theta \left ( n\log n \right )的。

然后下面给出一个过程详解:

给出数列:A\left [ 0,...7 \right ]=\left \{ 20,13,7,2,12,11,9,1 \right \} 

合并过程如下:

大整数乘法

对于位数大于100的十进制或二进制较大整数的乘法,我们先分析一下常规解法:

对于常规的计算方法,取问题规模衡量标准为乘数的位数n,则时间复杂度为\Theta \left ( n^{2} \right ),所以当这个乘数较大时,所花费的时间就会增大得比较快,故而希望有一种更优的算法。来解决这个问题。

那么我们试着从分治法的角度去思考一下这道题:

  • 首先将两个整数X,Y分别分成两部分,例如:X=1024,分为两部分XL=10,XR=24
  • 若X,Y为二进制数,则X=\left ( 2^{n/2}XL \right )+XRY=\left ( 2^{n/2}YL \right )+YR

X*Y=\left ( 2^{n/2}\left ( XL \right )+XR \right )*\left ( 2^{n/2}\left ( YL \right )+YR \right )

=2^{n}\left ( \left ( XL \right ) \left ( YL \right )\right )+2^{n/2}\left ( \left ( XL \right )\left ( YR \right ) +\left ( XR \right )\left ( YL \right )\right )+\left ( XR \right )\left ( YR \right )

  • 又因为

\left ( XL \right )\left ( YR \right ) + \left ( XR \right )\left ( YL \right ) =\left ( XL+XR \right )\left ( YL+YR \right ) -\left ( XL \right )\left ( XR \right )-\left ( YL \right )\left ( YR \right )

  • 那么这两个较大整数的乘法就换了一种形式,变成了由2的幂运算,以及乘法和加法组成的运算,而2幂运算是可以在线性时间内完成的(位运算),加法也是线性时间

那么由此我可以写出递归公式:

T\left ( n \right )=3T\left ( n/2 \right )+\Theta \left ( n \right )

其中先是将规模为n大整数乘法问题分治成了三个规模为n/2的子问题,子问题同样包含整数乘法,而子问题的解可以通过线性的操作时间合并,得到原问题的解

而如果使用主定理法则求解一下这个算法的时间复杂度,

可以求得该算法的时间复杂度为,T\left ( n \right )=\Theta \left ( n^{1.59} \right )

快速排序

快速排序是一个经典的体现分治法的算法

  • 选取基准值,根该值做划分
  • 递归地进行排序
  • 将各个划分将进行合并

快排模板

QuickSort( A[ l,...r ] )

{

        s = Partition( A[ l,...r ] )       //分

        QuickSort( A[ l,...s-1 ] )      //治

        QuickSort( A[ s,...r ] )

}

在讲解快排中的划分之前,我们先回忆一下Lomuto划分方法:

伪码描述:

LomutoPartition(A[l,...r]){
    p = A[l];
    s = l;
    for(int i=l+1; i<=r; i++){
        if(A[i]<p){
            s=s+1;
            swap(A[s],A[i])
        }
    }
    swap(A[l],A[s])
    return s
}

与Lomuto划分不同的是,快速排序使用的是霍尔划分(HoarePartition)

  • 与LomutoPartition不同的是,HoarePartition是通过两个指针i,j分别从数组的两端进行扫描
  • 取基准值p,i从左边遍历,当A[i]>=p时停止
  • j从右边开始遍历,当A[j]<=p停止
  • 然后swap(A[i],A[j])

这是我们理想的情况,但有可能碰到的一种情况就是i>=j(即两个指针相交了)。

所以针对这些特殊情况,我们需要做特殊处理:

  • 首先考虑i>j时,我们先分析一下,i只有碰见A[i]>=p才会停止,而j只有碰见A[j]<=p才会停止,所以请问i,j在这种情况下的位置关系是怎样的?

只有可能是我的划分已经完成了,那么此时我不能swap(A[j],A[i]),而是应该swap(A[j],p)

  • 还有一种更为特殊的情况就是,i=j,同样大家考虑一下不难得出结论,此时只可能A[i]=A[j]=p

所以可以将这两种情况合并,也就是在碰见指针相交的情况的话就直接swap(A[j],p)。

伪码描述:

HoarePartition(A[l,..r]){
    i=l; j=r+1;
    repeat{
        repeat i++ until A[i]>=p
        repeat j-- until A[j]<=p
        swap(A[i],A[j])
    } until i>=j
    swap(A[i],A[j])   //撤销指针相交的那次交换
    swap(A[l],A[j])   //与基准值做交换
    return j          //将基准值的正确位置返回
}

时间效率分析: 

最差情况:\Theta \left ( n^{2}\right )

最优(平均)情况:\Theta \left ( n\log n \right )

Strassen矩阵乘法

假定有两个n*n的矩阵A,B,C=AB,则有:

C_{ij}=\sum_{k=1}^{n}A\left [ i,k \right ]B\left [ k,j \right ]

也就是说,对于每一个C_{ij}都需要进行n次乘法,n-1次加法,那么对于n^2个元素来说,时间复杂度就是\Theta \left ( n^{3} \right )

这样的效率显然难以接受,Strassen通过分治技术,将时间复杂度降低了:

首先将n阶矩阵划分为四个部分,每个部分是n/2阶的矩阵,然后就会有: 

这样会进行8次乘法,以及4 次加法,但是不难通过主定理法计算得到这个算法的时间复杂度依然为\Theta \left ( n^{3} \right )

于是前人又开始动脑子了,他将刚才的8次乘法继续优化成了7次乘法:

这样的话递归方程式就可以写成:

T\left ( n \right )=7T\left ( n/2 \right )

T\left ( 1 \right )=1 

此时再通过主定理计算该算法的时间复杂度的话,就会发现时间复杂度由原来的3次方变成了2.81次方。虽然从数字上看起来并没有多么大的变化,但是当输入规模足够大时,节省的时间还是比较客观的。

以上是分治法的部分经典算法,当然还有像最近对问题、凸包问题的分治解法等问题后面会陆续更新。


原文地址:https://blog.csdn.net/MaverickCoder/article/details/143584460

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!