算法导论_第四章_分治策略

12 篇文章 0 订阅
10 篇文章 0 订阅

算法导论_第四章_分治策略

分治的三个步骤:

分解:将问题划分为一些子问题,子问题的形式与原问题一样,只是规模更小。

解决:递归的求解出子问题。如果子问题足够小,则停止递归,直接求解

合并:将子问题的解组合成原问题的解。


最大子数组问题:

给定数组A,寻找A的和最大的非空连续子数组。


可以利用暴力求解,其为Ω(n^2)

这里利用分治法解决,其时间复杂度为Θ(n*lg(n))

既然是分治,即肯定要把数组分开,其有三种情况:

1.完全位于左边的数组

2.完全位于右边的数组

3.跨越了中点


下面给出具体代码,已经加上详细注释:

/*************************************************************************
	> File Name: FIND_MAXIMUM_SUBARRAY.cpp
	> Author:chudongfang 
	> Mail:1149669942@qq.com 
	> Created Time: 2016年06月24日 星期五 08时53分20秒
 ************************************************************************/

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;


void FIND_MAX_CROSSING_SUBARRAY(int A[],int low,int mid ,int high);
void FIND_MAXIMUM_SUBARRAY(int A[],int low,int high);

int _low,_high,_sum;

int main(int argc,char *argv[])
{
    int A[1000]={13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
    FIND_MAXIMUM_SUBARRAY(A,0,15);
    printf("%d %d %d",_low,_high,_sum);
    
    return 0;
}
/***********************************
 * 函数功能:得到最大数组
 * 参数:
 * 1.数组
 * 2.数组最左边坐标
 * 3.数组最右边坐标
 * *********************************/

void FIND_MAXIMUM_SUBARRAY(int A[],int low,int high)
{
    int left_low,left_high,right_low,right_high;
    int left_sum,right_sum;
    int cross_low,cross_high,cross_sum;
    if(low==high)
    {
        _low=low;
        _high=high;
        _sum=A[low];
        return;
    }
    
    int mid=(low+high)/2;//分割点 
    
    FIND_MAXIMUM_SUBARRAY(A,low,mid);//找到第一种情况的最大数组
    left_low =_low;
    left_high=_high;
    left_sum =_sum;
    
    FIND_MAXIMUM_SUBARRAY(A,mid+1,high);//找到第二种情况的最大数组
    right_sum=_sum;
    right_high=_high;
    right_low=_low;

    FIND_MAX_CROSSING_SUBARRAY(A,low,mid,high);//找到第三种情况的最大数组
    cross_low=_low;
    cross_high=_high;
    cross_sum=_sum;
   //比较三种情况,哪种情况较优返回哪种情况
    if(left_sum>=right_sum&&left_sum>=cross_sum)
    {
        _low=left_low;
        _high=left_high;
        _sum=left_sum;
    }
    else if(right_sum>=left_sum&&right_sum>=cross_sum)
    {
        _low =right_low;
        _high=right_high;
        _sum =right_sum;
    }
    else
    {
        _low =cross_low;
        _high=cross_high;
        _sum =cross_sum;
    }
    return ;
}


/**********************************
 * 函数功能:寻找跨越中点的最大子数组
 * 参数:
 * 1.数组
 * 2.数组最左边
 * 3.数组中点
 * 4.数组最右边
 * *****************************/
void FIND_MAX_CROSSING_SUBARRAY(int A[],int low,int mid ,int high)
{
    int left_sum=-INF;//左边的最大数组,从中点开始
    int right_sum=-INF;//右边的最大数组,从中点开始
    int sum=0;
    int max_left,max_right;//最大数组左边坐标,最大数组右边坐标
    
    for(int i=mid;i>=low;i--)//左边最大数组求解
    {
        sum+=A[i];
        if(sum>left_sum)
        {
            left_sum=sum;
            max_left=i;
        }
    }

    sum=0;
    for(int i=mid+1;i<=high;i++)//右边最大数组求解
    {
        sum+=A[i];
        if(sum>right_sum)
        {
            right_sum=sum;
            max_right=i;
        }
    }

    //利用全局变量,代替return 
    _low=max_left;                 
    _high=max_right;             
    _sum=right_sum+left_sum;     
    return ;
}





我们来分析一下其时间复杂度:

可以看出,其把一个原问题分解为两个子问题,所以2T(n/2)

又其中的寻找 跨越中点的最大子数组的时间复杂度为Θ(n^2)

所以T(n)=2*T(n/2)+Θ(n^2)

所以根据上几节的知识,其时间复杂度为Θ(n*lg(n))




下面再来介绍一个例子:


矩阵乘法的Strassen算法


对于一个矩阵乘法其可以同过一下一个简单方法实现:


/*************************************************************************
	> File Name: Strassen.cpp
	> Author:chudongfang 
	> Mail:1149669942@qq.com 
	> Created Time: 2016年06月27日 星期一 09时55分03秒
 ************************************************************************/

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
void SQUARE_MATRIX_MULTIPLY(int A[][100],int B[][100],int C[][100],int n);
int main(int argc,char *argv[])
{

    int A[100][100];
    int B[100][100];
    int C[100][100];
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&A[i][j]);

    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&B[i][j]);
    SQUARE_MATRIX_MULTIPLY(A,B,C,n);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
            printf("%4d",C[i][j]);
        printf("\n");
    }


    return 0;
}
void SQUARE_MATRIX_MULTIPLY(int A[][100],int B[][100],int C[][100],int n)
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            C[i][j]=0;
            for(int k=1;k<=n;k++)
            {
                C[i][j]+=A[i][k]+B[k][j];
            }
        }
    }
}




其时间复杂度为

Θ(n^3);

下面采用分治方法计算:

由于该数组分治操作相对复杂,这里先给出伪代码,以后有时间再给出相应代码:




跟据代码可以看出其递归式为:

T(n)=8*(n/2)+Θ(n^2)


则根据主方法计算出其时间复杂度为Θ(n^3);

所以其分治并不能降低其时间复杂度


下面介绍一个可以降低其时间复杂度的方法

Strassen方法

其算法核心思想是利用空间换换时间,利用加减法减少递归次数,使其递归次数由8次降低到了7次。减少一次的代价

可能是额外的几次n/2*n/2矩阵的加法,但只是常数次。


下面给出其伪代码:







其根据加减法进行一些转换,最后的结果还是不变的。

这里给出书上的解释:











求解递归式

这里重点为主方法,其运用最广

1.代入法求解递归式

思想:

(1)猜测解的形式

(2)用数学归纳法,求出解中的常数,并证明解是正确的。

在这里,先跟据情况猜测出解的大概情况,然后代入,根据前面的对渐进符号的定义最终求出常数符合条件,则其等

式成立。


2.用递归树求解递归式

在递归树中,每个节点表示一个单一的子问题的代价,子问题对应某次递归函数调用。我们将树中每层代价求和,得

到每层代价,然后将所有层的代价求和,得到所有层次的递归调用的总代价。


3.利用主方法求解递归式

主方法为如下形式的递归式提供了一种“菜谱”式的求解方法

T(n)=a*T(n/b)+f(n)

其中a>=1b>1是常数,f(n)是渐进正函数。


主定理


a>=1b>1是常数,f(n)是一个函数,T(n)是定义在非负整数上的递归式:

T(n)=a*T(n/b)+f(n)

其中我们将n/b解释为向上取整或向下取整,那么T(n)有如下渐进界:


1.若对某个常数e>0f(n)=O(n^ log(b) a-e)则,T(n)=Θ(n^log(b)a)


2.f(n)=Θ(n^log(b)a)T(n)=Θ(n^log(b)a*lg(n))


3.若对某个e>0f(n)=Ω(n^(log(b)a-e))其对某个常数c<1和所有足够大的na*f(n/b)

<=c*f(n),T(n)=Θ(f(n))


简明一点,就是拿f(n)和函数n^(log(b)a)比较,

如果n^(log(b)a)更大,就满足情况1其解为:T(n)=Θ(n^log(b)a)


若函数f(n)更大,如情况3则解为:T(n)=Θ(f(n))


如果两个函数大小相当就为情况2,其解为

T(n)=Θ(n^log(b)a*lg(n))

以上比价都必须满足多项式大于或小于


这里举几个例子


a=9 b=3;

f(n)=n

应用情况1,所以其为Θ(n^2)


a=1 b=3/2 f(n)=1

应用于情况2所以其为Θ(lgn)



a=3 b=4 f(n)=n*lg(n)

应用于情况3所以其为Θ(n*lg(n))


a=2 b=2 f(n)= Θ(n)

应用于情况2所以其为Θ(n*lg(n))




a=8 b=2 f(n)= Θ(n^2)

应用于情况1所以其为Θ(n^3)




a=7 b=2 f(n)= Θ(n^2)

应用于情况1所以其为Θ(n^lg(7))


总结一下,其用法,比较f(n)n^(log(b)a),

如果不相同,则取其较大的那个,


如果相同 ,则取log(b)a再乘上lg(n)



下面还有对主定理的证明内容,由于我们先只是会运用主定理,这里先不证,留到以后

进阶时在进行分析。



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值