【算法分析与设计】递归与分治策略(一)

简介: 【算法分析与设计】递归与分治策略

一、学习要点

  理解递归的概念。

  掌握设计有效算法的分治策略

  通过下面的范例学习分治策略设计技巧。

  (1)二分搜索技术;

  (2)大整数乘法

  (3)Strassen矩阵乘法;

  (4)棋盘覆盖;

  (5)合并排序和快速排序;

  (6)线性时间选择;

  (7)最接近点对问题;

  (8)循环赛日程表。


二、算法总体思想

  对这k个子问题分别求解。如果子问题的规模仍然不够小,则 再划分为k个子问题如此递归的进行下去直到问题规模足够小,很容易求出其解为止

  将求出的小规模的问题的解合并为一个更大规模的问题的解,自底向上逐步求出原来问题的解

  分治法的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之


三、递归的概念

  直接或间接地调用自身的算法称为递归算法。用函数自身给出定义的函数称为递归函数

  由分治法 产生的子问题往往是原问题的较小模式,这就为使用递归技术提供了方便。在这种情况下,反复应用分治手段,可以使子问题与原问题类型一致而其规模却不断缩小,最终使子问题缩小到很容易直接求出其解。这自然导致递归过程的产生

  分治与递归像一对孪生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。


例1 阶乘函数

  阶乘函数可递归地定义为:

  边界条件与递归方程是递归函数的二个要素,递归函数只有具备了这两个要素,才能在有限次计算后得出结果


例2 Fibonacci数列

  无穷数列1,1,2,3,5,8,13,21,34,55,……,称为Fibonacci数列。它可以递归地定义为:

  第n个Fibonacci数可递归地计算如下:

int fibonacci(int n)
   {
       if (n <= 1) return 1;
       return fibonacci(n-1)+fibonacci(n-2);
   }

例3 Ackerman函数

  当一个函数及它的一个变量是由函数自身定义时,称这个函数是双递归函数

  Ackerman函数A(n,m)定义如下:

  前2例中的函数都可以找到相应的非递归方式定义:

  本例中的Ackerman函数却无法找到非递归的定义

  A(n,m)的自变量m的每一个值都定义了一个单变量函数:

  M=0时,A(n,0)=n+2

  M=1时,A(n,1)=A(A(n-1,1),0)=A(n-1,1)+2,和A(1,1)=2故A(n,1)=2*n

  M=2时,A(n,2)=A(A(n-1,2),1)=2A(n-1,2),和A(1,2)=A(A(0,2),1)=A(1,1)=2,故A(n,2)= 2^n 。

  M=3时,类似的可以推出:

  M=4时,A(n,4)的增长速度非常快,以至于没有适当的数学式子来表示这一函数

  定义单变量的Ackerman函数A(n)为,A(n)=A(n,n)。

  定义其拟逆函数α(n)为:α(n)=min{k|A(k)≥n}。即α(n)是使n≤A(k)成立的最小的k值。

  α(n)在复杂度分析中常遇到。对于通常所见到的正整数n,有α(n)≤4。但在理论上α(n)没有上界,随着n的增加,它以难以想象的慢速度趋向正无穷大。


例4 整数划分问题

  将正整数n表示成一系列正整数之和:n=n1+n2+…+nk,其中n1≥n2≥…≥nk≥1,k≥1。

  正整数n的这种表示称为正整数n的划分。求正整数n的不同划分个数

  例如正整数6有如下11种不同的划分:

  6;

  5+1;

  4+2,4+1+1;

  3+3,3+2+1,3+1+1+1;

  2+2+2,2+2+1+1,2+1+1+1+1;

  1+1+1+1+1+1。

  前面的几个例子中,问题本身都具有比较明显的递归关系,因而容易用递归函数直接求解

  在本例中,如果设p(n)为正整数n的划分数,则难以找到递归关系,因此考虑增加一个自变量:将最大加数n1不大于m的划分个数记作q(n,m)。可以建立q(n,m)的如下递归关系。

  (3) q(n,n)=1+q(n,n-1);

  正整数n的划分由n1=n的划分和n1≤n-1的划分组成。

  (4) q(n,m)=q(n,m-1)+q(n-m,m),n>m>1;

  正整数n的最大加数n1不大于m的划分由n1=m的划分和n1≤n-1 的划分组成。

  正整数n的划分数p(n)=q(n,n)


例5 Hanoi塔问题

  设a,b,c是3个塔座。开始时,在塔座a上有一叠共n个圆盘,这些圆盘自下而上,由大到小地叠在一起。各圆盘从小到大编号为1,2,…,n,现要求将塔座a上的这一叠圆盘移到塔座b上,并仍按同样顺序叠置。在移动圆盘时应遵守以下移动规则:

  规则1:每次只能移动1个圆盘;

  规则2:任何时刻都不允许将较大的圆盘压在较小的圆盘之上;

  规则3:在满足移动规则1和2的前提下,可将圆盘移至a,b,c中任一塔座上。

void hanoi(int n, int a, int b, int c)
   {
       if (n > 0)
       {
          hanoi(n-1, a, c, b);
          move(a,b);
          hanoi(n-1, c, b, a);
       }
   }

递归小结

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

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

  解决方法:在递归算法中消除递归调用,使其转化为非递归算法

  1、采用一个用户定义的栈来模拟系统的递归调用工作栈。该方法通用性强,但本质上还是递归,只不过人工做了本来由编译器做的事情,优化效果不明显。

  2、用递推来实现递归函数

  3、通过变换能将一些递归转化为尾递归,从而迭代求出结果。

  后两种方法在时空复杂度上均有较大改善,但其适用范围有限。


四、分治法

1、分治法的适用条件

  分治法所能解决的问题一般具有以下几个特征:

  该问题的规模缩小到一定的程度就可以容易地解决

  该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质利用该问题分解出的子问题的解可以合并为该问题的解;

  该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。

  这条特征涉及到分治法的效率,如果各子问题是不独立的,则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然也可用分治法,但一般用动态规划较好。

divide-and-conquer(P)
  {
    if ( | P | <= n0) adhoc(P);   //解决小规模的问题
    divide P into smaller subinstances P1,P2,...,Pk;//分解问题
    for (i=1,i<=k,i++)
      yi=divide-and-conquer(Pi);  //递归的解各子问题
    return merge(y1,...,yk);  //将各子问题的解合并为原问题的解
  }

  人们从大量实践中发现,在用分治法设计算法时,最好使子问题的规模大致相同。即将一个问题分成大小相等的k个子问题的处理方法是行之有效的。这种使子问题规模大致相等的做法是出自一种平衡(balancing)子问题的思想,它几乎总是比子问题规模不等的做法要好。

  一个分治法将规模为n的问题分成k个规模为n/m的子问题去解。设分解阀值n0=1,且adhoc解规模为1的问题耗费1个单位时间。再设将原问题分解为k个子问题以及用merge将k个子问题的解合并为原问题的解需用f(n)个单位时间。用T(n)表示该分治法解规模为|P|=n的问题所需的计算时间,则有:

  通过迭代法求得方程的解:

  注意:递归方程及其解只给出n等于m的方幂时T(n)的值,但是如果认为T(n)足够平滑,那么由n等于m的方幂时T(n)的值可以估计T(n)的增长速度。通常假定T(n)是单调上升的,从而当mi≤n<mi+1时,T(mi)≤T(n)<T(mi+1)。


相关文章
|
1天前
|
缓存 算法 搜索推荐
Java中的算法优化与复杂度分析
在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
15 6
|
26天前
|
数据采集 存储 算法
Python 中的数据结构和算法优化策略
Python中的数据结构和算法如何进行优化?
|
23天前
|
算法
通过matlab分别对比PSO,反向学习PSO,多策略改进反向学习PSO三种优化算法
本项目使用MATLAB2022A版本,对比分析了PSO、反向学习PSO及多策略改进反向学习PSO三种优化算法的性能,主要通过优化收敛曲线进行直观展示。核心代码实现了标准PSO算法流程,加入反向学习机制及多种改进策略,以提升算法跳出局部最优的能力,增强全局搜索效率。
|
25天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
56 1
|
1月前
|
算法
优化策略:揭秘钢条切割与饼干分发的算法艺术
本文探讨了钢条切割与饼干分发两个经典算法问题,展示了算法在解决实际问题中的应用。钢条切割问题通过动态规划方法,计算出不同长度钢条的最大盈利切割方式,考虑焊接成本后问题更为复杂。饼干分发问题则采用贪心算法,旨在尽可能多的喂饱孩子,分别讨论了每个孩子一块饼干和最多两块饼干的情况。这些问题不仅体现了数学的精妙,也展示了工程师的智慧与创造力。
38 4
|
1月前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
53 2
|
1天前
|
机器学习/深度学习 算法
基于改进遗传优化的BP神经网络金融序列预测算法matlab仿真
本项目基于改进遗传优化的BP神经网络进行金融序列预测,使用MATLAB2022A实现。通过对比BP神经网络、遗传优化BP神经网络及改进遗传优化BP神经网络,展示了三者的误差和预测曲线差异。核心程序结合遗传算法(GA)与BP神经网络,利用GA优化BP网络的初始权重和阈值,提高预测精度。GA通过选择、交叉、变异操作迭代优化,防止局部收敛,增强模型对金融市场复杂性和不确定性的适应能力。
101 80
|
20天前
|
算法
基于WOA算法的SVDD参数寻优matlab仿真
该程序利用鲸鱼优化算法(WOA)对支持向量数据描述(SVDD)模型的参数进行优化,以提高数据分类的准确性。通过MATLAB2022A实现,展示了不同信噪比(SNR)下模型的分类误差。WOA通过模拟鲸鱼捕食行为,动态调整SVDD参数,如惩罚因子C和核函数参数γ,以寻找最优参数组合,增强模型的鲁棒性和泛化能力。
|
26天前
|
机器学习/深度学习 算法 Serverless
基于WOA-SVM的乳腺癌数据分类识别算法matlab仿真,对比BP神经网络和SVM
本项目利用鲸鱼优化算法(WOA)优化支持向量机(SVM)参数,针对乳腺癌早期诊断问题,通过MATLAB 2022a实现。核心代码包括参数初始化、目标函数计算、位置更新等步骤,并附有详细中文注释及操作视频。实验结果显示,WOA-SVM在提高分类精度和泛化能力方面表现出色,为乳腺癌的早期诊断提供了有效的技术支持。
|
6天前
|
供应链 算法 调度
排队算法的matlab仿真,带GUI界面
该程序使用MATLAB 2022A版本实现排队算法的仿真,并带有GUI界面。程序支持单队列单服务台、单队列多服务台和多队列多服务台三种排队方式。核心函数`func_mms2`通过模拟到达时间和服务时间,计算阻塞率和利用率。排队论研究系统中顾客和服务台的交互行为,广泛应用于通信网络、生产调度和服务行业等领域,旨在优化系统性能,减少等待时间,提高资源利用率。