• 算法打基础——分治法


    第三讲主要是讲divide-and-conquer, 与上一讲结合的很紧密,因为分治法几乎都是递归啦,求复杂度必备啊!

     这一讲的主要知识点有:

    1.分治法主要步骤 (后面就全是分治法的应用了)2.二分搜索 3.快速求幂 4.快速求斐波那契数列  5.矩阵连乘(Strassen's algorithm)   下面分别来介绍

    分治法的主要步骤: 分为三步。

    1. 将问题分解成子问题

    2.递归的去解决这些子问题

    3. 合并这些子问题

    举前面的归并排序来说,这是非常典型的分治法。

    1.Divide:Trivial.

    2.Conquer:Recursively sort 2subarrays.

    3.Combine:Linear-time merge

    复杂度:T(n)=2T(n/2)+Θ(n) 分析:2-子问题个数 n/2-子问题规模 Θ(n)-合并以及其他处理

    二分搜索:

    Find an element in a sorted array:

    1.Divide:Check middle element.

    2.Conquer:Recursively search 1subarray.

    3.Combine:Trivial.

     

    对二分查找的分析:T(n) = T(n/2)+Θ(1): 由主定理 case2 可以得到复杂度T(n)=Θ(logn)

    快速求幂:Compute an, where n ∈N.

    一般解法当然就是做n次乘法,复杂度是Θ(n). 

    用分治法来处理,将其分成n/2的规模来递归解决,其方案是:

    an=an/2 ⋅an/2                     if n is even;

          a(n–1)/2 ⋅a(n–1)/2 ⋅a   if n is odd.

    分成这两种情况来解决,注意 因为乘法的两边都是相同的数,所以只用计算一次,故他的复杂度是: T(n)=T(n/2)+Θ(1) 即 T(n)=Θ(logn)

    快速求斐波那契数列:这个数列是非常重要的数列,给出他的定义

     

     如果按照这个递归式去解这个问题,复杂度将是:Ω(Kn) (即指数时间),其中K=(1+sqr(5))/2;这是非常高的一个复杂度。

    还可以按照自底向上的步骤:按顺序计算F0,F1,F2,...Fn。由于之前那个递归式需要处理大量重复的子式,而现在不用了,其复杂度是Θ(n)。

    或者用一个一般式来求,即Fn=Kn/sqr(5) 且四舍五入到最近整数。这种算法的就是计算一个数的n次方,最快也可以达到Θ(log n)。但是这个方法不可靠!因为计算机的浮点运算可能会导致错误的四舍五入结果!

    然后这里引入了一个非常神奇的定理:

    用这个定理去求斐波那契数列的第n项就不用涉及到浮点运算了,而且这是n个2*2矩阵乘法,其复杂度同数的n次幂,所以复杂度是Θ(log n)

    这个定理的证明可以使用数学归纳法,很容易证明。

    矩阵乘法(strassen's 公式)

     一般的,求矩阵乘法的运算是:

    使用伪代码表示为: 

    for i=1 to n

       do for j=1 to n

          do cij = 0

              for k=1 to n

                  do cij = cij + aik * bkj

     由此也可以得到复杂度是 Θ(n3)

    我们想使用分治法的思想去解决矩阵乘法问题,所以首先将n*n的矩阵分开,分成4块。

    根据矩阵运算规则,我们也知道我们将n*n矩阵运算,分解成了8个(n/2)*(n/2)的乘法,以及4个两个同样的小矩阵的加法(矩阵加法的复杂度是那n2)

    可以得到新的复杂度是: T(n) = 8T(n/2) + Θ(n2)

    这个递推式可以根据主定理的case 1,得出复杂度仍然是 Θ(n3)

    由此我们给出Strassen's idea: 其主要思想是加法的复杂度是n2,乘法的复杂度是n3,所以尽量减少乘法,增加加法的个数。最后搞了一个7个乘法的表达式。

    T(n) = 7T(n/2) + Θ(n2)。  T(n) = Θ(nlg7) 降低了复杂度

     最后附上这章的自己写的算法代码:

     1 /////////////////////////CLRS   video lec3  二分搜索/////////////////////////////////////////////////
     2 //二分搜索是针对已排序序列的哦!O(lg n)的复杂度  /////////////////////////////////////////////////////
     3 
     4 
     5 #include<iostream>
     6 using namespace std;
     7 
     8 int main()
     9 {
    10     int arr[10] ={1,4,5,7,9,10,15,19,29,35};
    11     int find,flag=1,middle;
    12     cin>>find;
    13     int a=0,b=9;               // a,b 是左右的边界,在这个范围内搜索
    14     while(a<b)                 //这个判结束的条件很重要!!
    15     {
    16         middle = (a+b)/2;
    17         if(arr[middle]>find)
    18             b=middle-1;
    19         else if(arr[middle]<find)
    20             a=middle+1;
    21         else{
    22             cout<<middle<<endl;
    23             break;
    24         }
    25     }
    26     return 0;
    27 }
    Binarysearch
     1 /////////////////////////CLRS   video lec3  快速求幂/////////////////////////////////////////////////
     2 //O(lg n)的复杂度  /////////////////////////////////////////////////////
     3 
     4 
     5 #include<iostream>
     6 using namespace std;
     7 
     8 long mypower(int a,int n)
     9 {
    10     if(n==1) return a;
    11     else{
    12         long result;
    13         if(n%2==0){
    14             result = mypower(a,n/2);
    15             return result*result;
    16         }
    17         if(n%2==1){
    18             result = mypower(a,(n-1)/2);
    19             return result*result*a;
    20         }
    21     }
    22 }
    23 
    24 int main()
    25 {
    26     int a=6,n;
    27     cin>>n;
    28     long result = mypower(a,n);
    29     cout<<result<<endl;
    30 }
    快速求幂

  • 相关阅读:
    TIF转JPG
    跨线程取出控件的值的写法(不是跨线程赋予控件值)
    oracle根据正则表达式查找对应的字段
    oracle数据库连接字符串
    access检测表没有的字段,添加之
    解决 Unable to load DLL 'OraOps9.dll': 找不到指定的模块。 (Exception from HRESULT: 0x8007007E)
    oracle关键字使用
    to_number,Extract oracle的关键字
    OracleCommand.CommandText 无效
    调用带参数的线程两种方法
  • 原文地址:https://www.cnblogs.com/soyscut/p/3376838.html
Copyright © 2020-2023  润新知