算法设计与分析实验——分治法

一、实验目的

1 掌握分治法的设计思想(划分、分解、合并)

2 掌握分治法的具体实现和时间复杂度分析

3 理解分治法的常见特性

  • 实验内容

1 完成一维数组求和问题

2 求a的n次方   

       3 计算一位数组最大值的问题

       4 快速排序

       5 无序数组中查找数值k问题

       6 大整数相乘问题

三、问题分析[按照实现内容的等级, 分开写,并体现每个同学的任务]

1、一维数组求和:

   利用分治法将数组分成两个部分,然后利用递归对两个部分分别求和,最后再相加起来,就能求得总和;

2、求a的n次方

   首先判断n是偶数还是奇数,如果n是偶数,就直接分成两个a的n/2相乘,利用递归求解;如果是奇数,就分成两个a的(n-1)/2次方和a相乘,然后再利用递归求解;

3、计算一维数组最大值的问题

   先处理递归的出口,然后计算中点值,将数组分成两个部分,利用递归分别求出其最大值,最后再将这两个最大值进行比较,返回较大的值;

4、快速排序

   首先对待排序记录序列进行划分,划分的轴值应该遵循平衡子问题的原则,使划分后两个子序列的长度尽量相等

找到轴值之后,以轴值为基准将待排序序列划分为两个子序列,对每一个子序列分别进行递归处理

5、无序数组查找数值k的问题

   因为无序数组不方便对其直接查找,所以先对无序数组进行排序,利用快速排序的方法,将其排成有序数组之后,计算数组下标的中点,判断中点值是不是要查找的数,若是,则直接返回中点;然后再判断中点值和k的大小,分成两个区间利用递归去找,找到返回即可;

6、大整数相乘

   现有两个大数x,y,首先先将x,y分别拆开成为两部分,可以的x1,x0,y1,y0,然后再分别去找x和y的位数,将大整数的乘法分解成若干个小整数相乘,最后再一起加起来。

四、问题解决[按照实现内容的等级, 分开写,并体现每个同学的任务]

       1、一维数组求和

       (1)、算法DivideSum()

              输入:一维整形数组啊a[ ],数组左下标l,数组右下标r

              输出:整形数组的和

              过程:1、如l==r,则返回a[l];

                       2、计算中间值m=(l+r)/2;

                       3、调用DivideSum(a,l,m)得到前半部分和sum1

                       4、调用DivideSum(a,m+1,r)得到后半部分和sum2

                       5、返回sum1+sum2;

       (2)、代码:

public class ch1 {
    /*
    一维数组求和
    */
    int DivideSum(int []a,int l,int r)
    {
        if (l==r) {
            return a[l];
        }
        int m=(l+r)/2;
        int sum1=DivideSum(a,l,m);  /*  求前一半  */
        int sum2=DivideSum(a,m+1,r);   /*  求后一半  */
        int sum;
        sum = sum1+sum2;
        return sum;
    }

}

该算法的时间复杂度为:O(log2n)

       (3)、运行截图

2、求a的n次方

(1)、算法getSum(int a,int n)

       输入:底数a,幂n

       输出:a的n次方

       过程:1、如n==1,则返回a;

                2、如n%2==0,则返回调用getSum(a,n/2)*getSum(a,n/2)得的值

                3、如n%2!=0,则返回调用getSum(a,(n-1)/2)*getSum(a,(n-1)/2)*a得的值

(2)、代码:

public class ch2 {
    /*
    求a的n次方
    */
    int getSum(int a,int n){
        if (n==1) {
            return a;
        }
        else if (n%2==0)
        {
            return getSum(a,n/2)*getSum(a,n/2);/*   n如果是偶数   */
        }
        else
        {
            return getSum(a,(n-1)/2)*getSum(a,(n-1)/2)*a;   /*   n如果是奇数   */
        }

    }

}

该算法时间复杂度为O(log2n)

(3)、运行截图:

3、计算一维数组的最大值

(1)、算法:getMax(int []a,int l,int r)

       输入:一位整形数组a[],数组左下标l,数组右下标r

       输出:数组最大值max

       过程:1、如l==r,则返回a[l];

                2、计算划分中点m=(l+r)/2;

                3、调用getMax(a,l,m)得前半区间最大值lmax

                4、调用getMax(a,m+1,r)得后半区间最大值rmax

                5、如lmax>rmax,则返回lmax,否则返回rmax、

(2)、代码:

public class ch3 {
    /*
    求一维数组最大值
    */
    int getMax(int []a,int l,int r){
        if (l==r)
            return a[l];
        else
        {
            int m=(l+r)/2;
            int lMax=getMax(a,l,m);
            int rMax=getMax(a,m+1,r);
            if (lMax>rMax)
                return lMax;
            else
                return rMax;
        }

    }
}

该算法的时间复杂度为:O(log2n)

(3)、运行截图:

4、快速排序:

(1)、算法:设函数Partition实现对序列r[first]~r[end]进行划分,函数QuiSort实现快速排序;对于该算法的时间复杂度,最好的情况下,每次划分对一个记录定位后,该记录的左侧子序列与右侧子序列的长度相同。对于n个记录的序列中有:

T(n)=2T(n/2)+n=……= O(nlog2n)

而对于最坏情况下,待排序记录序列正序或者逆序,每次划分只得到一个比上依次划分少一个记录的子序列,此时的时间复杂度为O(n2)

平均下来时间复杂度为O(nlog2n)

(2)、代码:public class ch5 {
    int Partition(int []r,int first,int end){
        int temp,i=first,j=end;
        while (i<j)
        {
            while (i<j && r[i]<=r[j] ) {
                j--;
            }
                if(i<j)
                {
                    temp = r[i];
                    r[i]=r[j];
                    r[j]=temp;
                    i++;
                }
            while (i<j && r[i]<=r[j]){
                i++;
            }
                if (i<j)
                {
                    temp = r[i];
                    r[i]=r[j];
                    r[j]=temp;
                    j--;
                }
        }
        return i;
    }
    void QuickSort(int []r,int first,int end){
        if (first<end)
        {
            int pivot =Partition(r,first,end);
            QuickSort(r,first,pivot-1);
            QuickSort(r,pivot+1,end);
        }


    }
}

(3)、运行截图:

5、无序数组查找元素k

(1)、算法:无序数组可以先用上述快速排序现将其变成有序数组,之后在对其查找

              算法:SearchK(int a[],int l,int r,int k)

              输入:一位整形数组a[],数组左下标l,数组右下标r

              输出:数组中与元素k相同的值的下标

              过程:1、计算中点m=(l+r)/2

                       2、如a[m]==k,则返回m;

                       3、如a[m]>k,则返回调用SearchK(a,l,m-1);

                       4、如a[m]<k,则返回调用SearchK(a,m+1,r);

       (2)、代码:

public class ch4 {
    /*
    无序数组查找k
     */
    int searchK(int []a,int l,int r,int k){
        int m = (l+r)/2;
        if (l<=r){
            if (a[m]==k){
                return m;
            }
            else if(a[m]>k)
            {
                return searchK(a,l,m-1,k);
            }
            else
            {
                return searchK(a,m+1,r,k);
            }
        }
        else {
            return -1;
        }

    }
}

该算法的时间复杂度为:O(log2n)

       (3)、运行截图:

       实验1~5的主函数:

public class Main {
    public static void main(String[] args) {
        ch1 cha1 = new ch1();
        Scanner sc = new Scanner(System.in);
        int n;
        System.out.println("请输入数组长度:");
        n=sc.nextInt();
        int []m= new int[n];
        System.out.println("请输入数组元素:");
        for (int i=0;i<m.length;i++){
            m[i]= sc.nextInt();
        }
        int k;
        k=cha1.DivideSum(m,0,n-1);
        System.out.println("一维数组之和为:"+k);
        /*------------------------------------------*/
        ch3 cha3 = new ch3();
        int max=cha3.getMax(m,0,n-1);
        System.out.println("该数组的最大值为:"+max);
        /*------------------------------------------*/
        ch5 cha5 = new ch5();
        cha5.QuickSort(m,0,n-1);
        System.out.println("数组快速排序后为(升序排序):");
        System.out.println(Arrays.toString(m));
        /*-------------------------------------------*/
        ch4 cha4 = new ch4();
        System.out.println("请输入你要查找的元素k:");
        int kk=sc.nextInt();
        System.out.println("k的下标为:"+cha4.searchK(m,0,n-1,kk));
        /*------------------------------------------------*/
        ch2 cha2 = new ch2();
        int x,y;
        System.out.println("请输入底数a");
        x=sc.nextInt();   /*底数*/
        System.out.println("请输入幂n");   /* 幂 */
        y=sc.nextInt();
        int sum=cha2.getSum(x,y);
        System.out.println("a的n次方为"+sum);
        /*---------------------------------------------------*/

}
}

       6、大整数相乘:

       (1)、对于x=1234,y=5678,首先先将x拆成a和b两部分,将y拆为c和d两部分,然后执行Karatsuba算法,计算过程如下:

       步骤1:计算a*c

       步骤2:计算b*d

       步骤3:计算(a+b)*(c+d)

       步骤4:步骤3的结果减去步骤1和步骤2的结果

       步骤5:计算a*c*104 +步骤4的结果*102  +b*d

当整数很大时就一直拆分,用递归一直拆分,直到整数足够小之后进行求解

       (2)、代码:

#include<iostream>

#include<cmath>

#include<stdio.h>

using namespace std;

//找到x的位数 

int size(long x){ 

    int num=0; 

    do{ 

        num++; 

        x=x/10; 

    }while(x); 

    return num; 

long Karatsuba(long x,long y)

{

      if(x<10 || y<10) return x*y;

      else{

             long m , x1 , x0 ,y1 , y0 , z0 , z1 , z2 ;

             m=max(size(x),size(y))/2;

             x1 = x/(int)pow(10,m);

             x0 = x-x1*(int)pow(10,m);

             y1 = y/(int)pow(10,m);

             y0 = y-y1*(int)pow(10,m);

             z2 = Karatsuba(x1,y1);

             z0 = Karatsuba(x0,y0);

             z1 = Karatsuba((x1+x0),(y1+y0))-z2-z0;

             return z2*(int)pow(10,2*m)+z1*(int)pow(10,m)+z0;

      }

}

int main()

{

      long a , b;

      cin >> a >> b;

      cout << Karatsuba(a,b) << endl;

      return 0;

}

该算法的时间复杂度为:O(log3n)

       (3)、运行截图:

五、实验结果总结

回答以下问题:

  1. 分治法实现的复杂度一定比蛮力法优吗?举例说明

分治法的时间复杂度一般情况下比蛮力法要好,但是也不是所有的分治法的时间复杂度都比蛮力法低,例如,蛮力法的选择排序时间复杂度只有O(n),而分治法的归并排序的时间复杂度确实O(nlog2n)

  1. 分治法实现一定要用递归吗?你能实现一个不用递归的分治吗?

分治法也不一定要使用递归的方法,例如:有序数组查找元素k

public static int k(int[]number,int k,int r) {
int low,high,mid;
low=0;
high=r-1;
while(low<=high) {
mid=(low+high)/2;
if(number[mid]==k)return mid;
if(number[mid]>k)high=mid-1;
if(number[mid]<k)low= mid+1;
}
return 0;
}

这也是分治法,但是它并没有应用到递归。

  1. 假定原问题的规模为n,分解的子问题的规模为n/b,分解的子问题的个数为a,a与b之间的大小关系如何,举例说明。

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

T(n)=a*blogbn-1

  1. 通过实验叙述你对分治法的理解及其优缺点。

分治法是一种将一个复杂的问题分解成若干个规模较小、相互独立,但类型相同的子问题求解;然后再将各种子问题的解组合成原始问题的一个完整答案。

它的优点有:结构清晰,可读性强,而且容易用数学归纳法来证明算法的正确性,因此它为设计算法、调试程序带来很大方便

它的缺点有:递归算法的运行效率较低,无论是耗费的计算时间还是占用的存储空间都比非递归算法要多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值