1、递归法
所谓递归,就是指如果需要求解当前状态就需要求解其依赖的迁移状态。
一般来说,递归需要有边界条件、递归前进段和递归返回段。
当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
采用递归描述的算法通常有这样的特征:
1)为求解规模为N的问题,设法将它分解成规模较小的问题;
2)然后从这些小问题的解方便地构造出大问题的解,并且这些规模较小的问题也能采用同样的分解和综合方法,分解成规模更小的问题,并从这些更小问题的解构造出规模较大问题的解。
3)这样的分解方法具有收敛性。即存在一个递归返回状态。
例1.1 求最大公约数--欧几里德算法
// 欧几里德算法--求两个整数的最大公约数 int gcd(int m, int n) { if(n == 0) return m; return gcd(n, m % n); }
2、穷举法
所谓穷举法就是遍历所有可能的状态。
例如:密码的暴力破解法就是采用这种方法。
3、化归法
所谓化归法是指,不直接解决原问题,而是把所要解决的问题,经过某种变化,使之归结为另一个(问题*),再通过(问题*)的求解,把解得结果作用于原有问题,从而使原有问题得解。
化归的原则是以已知的、简单的、具体的、特殊的、基本的知识为基础,将未知的化为已知的,复杂的化为简单的,抽象的化为具体的,一般的化为特殊的,非基本的化为基本的,从而得出正确的解答.
此在数学上的应用非常常见。同时,一个逻辑问题的解决的一些列子步骤不正是化归法的体现吗?
4、迭代法
迭代法也称辗转法,是一种不断用变量的旧值递推新值的过程。
最常见的迭代法是牛顿法。其他还包括最速下降法、共轭迭代法、变尺度迭代法、最小二乘法、线性规划、非线性规划、单纯型法、惩罚函数法、斜率投影法、遗传算法、模拟退火等等。
利用迭代算法解决问题,需要做好以下三个方面的工作:
1)迭代变量:在可以用迭代算法解决的问题中,至少存在一个直接或间接地不断由旧值递推出新值的变量,这个变量就是迭代变量。
2)迭代关系:指如何从变量的前一个值推出其下一个值的公式(或关系)。迭代关系式的建立是解决迭代问题的关键,通常可以使用递推或倒推的方法来完成。
3)迭代过程:迭代过程的控制通常可分为两种情况:一种是所需的迭代次数是个确定的值,可以计算出来;另一种是所需的迭代次数无法确定。对于前一种情况,可以构建一个固定次数的循环来实现对迭代过程的控制;对于后一种情况,需要进一步分析出用来结束迭代过程的条件。
迭代法都可以转为递归法。迭代法可以理解为具有递归性质的非递归解法。当然,非递归解法还可利用栈的思想。
例4.1 兔子数量问题
描述:一个饲养场引进一只刚出生的新品种兔子,这种兔子从出生的下一个月开始,每月新生一只兔子,新生的兔子也如此繁殖。如果所有的兔子都不死去,问到第 12 个月时,该饲养场共有兔子多少只?
分析: 这是一个典型的递推问题。我们不妨假设第 1 个月时兔子的只数为 u 1 ,第 2 个月时兔子的只数为 u 2 ,第 3 个月时兔子的只数为 u 3 ,……根据题意,“这种兔子从出生的下一个月开始,每月新生一只兔子”,则有
u 1 = 1 , u 2 = u 1 + u 1 × 1 = 2 , u 3 = u 2 + u 2 × 1 = 4 ,……
根据这个规律,可以归纳出下面的递推公式:
u n = u( n - 1 )× 2 (n ≥ 2)
// 兔子数量 /* 一个饲养场引进一只刚出生的新品种兔子,这种兔子从出生的下一个月开始, 每月新生一只兔子,新生的兔子也如此繁殖。 如果所有的兔子都不死去,问到第 12 个月时,该饲养场共有兔子多少只? */ int Rabbit(int u1, int time) { int x = u1; for(int i = 2; i <= time; i++) { x = x * 2; } return x; }
5、分治法
所谓分治法,就是分而治之。将一个问题分解为多个规模较小的子问题,这些子问题互相独立并与原问题解决方法相同。递归解这些子问题,然后将这各子问题的解合并得到原问题的解。
适用问题的特征:
- 该问题的规模缩小到一定的程度就可以容易地解决
- 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。所谓最优子结构是指:问题的最优解所包含的子问题的解也是最优的。
- 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题
一般实现步骤:分解--->递归求解--->合并
例5.1 河内塔问题
// 河内塔问题 a -> c void honi(int n, char a, char b, char c) { if(n == 1) { printf_s("move %d form %c to %c/n", n, a, c); } else { honi(n-1, a, c, b); printf_s("move %d form %c to %c/n", n, a, c); honi(n-1, b, a, c); } }
例5.2 归并排序
6、动态规划
将待求解问题分解成若干个子问题,但是经分解得到的子问题往往不是互相独立的,如果能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,就可以避免大量重复计算。
适合问题的特征:
- 在递归计算中,许多子问题将被重复计算多次
- 具有最优子结构性质。所谓最优子结构是指:问题的最优解所包含的子问题的解也是最优的。即满足最优化原理。
解决步骤:
1)找出最优解性质,划分子问题
2)递归的定义每阶段最优值
3)递归计算最优值
4)根据计算最优值时得到的信息,构造最优解
例6.1 求最长公共子串
例6.2 0-1背包问题
7、贪心算法
动态规划是贪心算法的基础。贪心算法是通过做一系列的选择来给出某一问题的最优解。对算法中的每一个决策点,做一个当时(看起来是)最佳的选择。这种启发式策略并不总是能产生出最优解。
一般步骤:
1)决定问题的最优子结构
2)设计出一个递归解
3)证明在递归的任一阶段,最优选择之一总是贪心选择。那么,做贪心选择总是安全的。
4)证明通过做贪心选择,所有子问题(除一个以外)都为空。
5)设计出一个实现贪心策略的递归算法。
6)将递归算法转换成迭代算法。
通常直接做出贪心选择来构造子结构,以产生一个待优化解决的子问题。更一般地,可以根据以下步骤设计贪心算法:
1)将优化问题转化成一个这样的问题,即先做出选择,再解决剩下的一个子问题。
2)证明原问题总是有一个最优解是做贪心选择得到的,从而说明贪心选择是安全的。
3)说明在做出贪心选择后,剩余的子问题具有这样的一个性质。即如果将子问题的最优解和我们所作的贪心选择联合起来,可以得出原问题的一个最优解。
贪心选择性质:一个全局最优解可以通过局部最优(贪心)选择来达到。换句话说,当考虑做如何选择时,我们只考虑对当前问题的选择而不考虑子问题的结果。
在动态规划中,每一步都要做出选择,但是这些选择依赖于子问题的解。因此,解动态规划问题一般是自底向上,从小子问题处理至大子问题。在贪心算法中,我们所做的总是当前看似最佳的选择,然后再解决选择之后的所出现的子问题。贪心算法所作的当前选择可能要依赖与已经作出的选择,但不依赖于有待于做出的选择或子问题的解,因此贪心策略通常是自顶向下地做的,一个一个地做出贪心选择,不断地将给定的问题实例归约为更小的问题。
最优子结构:对一个问题来说,如果它的一个最优解包含了其子问题的最优解,则称该问题具有最优子结构。
例7.1 部分背包问题
描述:把一系列的物品放入总量限制的包里,要求价值最大。但是物品可以分割为一部分。
8、回溯法
回溯法也称试探法,它的基本思想是:从问题的某一种状态(初始状态)出发,搜索从这种状态出发所能达到的所有“状态”,当一条路走到“尽头”的时候(不能再前进),再后退一步或若干步,从另一种可能“状态”出发,继续搜索,直到所有的“路径”(状态)都试探过。这种不断“前进”、不断“回溯”寻找解的方法,就称作“回溯法”。
用回溯算法解决问题的一般步骤为:
一、定义一个解空间,它包含问题的解。
二、利用适于搜索的方法组织解空间。
三、利用深度优先法搜索解空间。
四、利用限界函数避免移动到不可能产生解的子空间。问题的解空间通常是在搜索问题的解的过程中动态产生的,这是回溯算法的一个重要特性
回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。
9、分支限界法
分支限界 (branch and bound) 算法是一种在问题的解空间树上搜索问题的解的方法。但与回溯算法不同,分支定界算法采用广度优先或最小耗费优先的方法搜索解空间树,并且,在分支定界算法中,每一个活结点只有一次机会成为扩展结点。利用分支定界算法对问题的解空间树进行搜索,它的搜索策略是:
1 .产生当前扩展结点的所有孩子结点;
2 .在产生的孩子结点中,抛弃那些不可能产生可行解(或最优解)的结点;
3 .将其余的孩子结点加入活结点表;
4 .从活结点表中选择下一个活结点作为新的扩展结点。如此循环,直到找到问题的可行解(最优解)或活结点表为空。
分支限界法的思想是:首先确定目标值的上下界,边搜索边减掉搜索树的某些支,提高搜索效率。
例9.1 旅行商问题
问题描述:给定n个城市,有一个旅行商从某一城市出发,访问每个城市各一次后再回到原出发城市,要求找出的巡回路径最短。
reference