浅析几种常见算法(转载)

参考博客:https://blog.csdn.net/Bloodyrabbits/article/details/78445036 


一、递归与分支策略

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

2.如果原问题可分割成k个子问题(1<k≤n),且这些子问题都可解,并可利用这些子问题的解求出原问题的解,那么这种分治法就是可行的。

3.由分治法产生的子问题往往是原问题的较小模式,这就为使用递归技术提供了方便。

4.程序直接或间接调用自身的编程技巧称为递归算法 (Recursion)。

5.递归通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。

6.递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。

7.递归优秀在代码简洁,但首先需要有清晰的思路理清递归的循环条件及出口。

8.有时递归需要多次重复某一计算,可以用数组记录下需要的数据进行调用大大降低空间复杂度。

9.递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

10.分治法在每一层递归上都有三个步骤:

分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;

解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;

合并:将各个子问题的解合并为原问题的解。

11.在用分治法设计算法时,最好使子问题的规模大致相同。如分成大小相等的k个子问题,许多问题可以取k=2。

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

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

(2)该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;

(3)利用该问题分解出的子问题的解可以合并为该问题的解;

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

二、贪心算法

1.在求最优解问题的过程中,依据某种贪心标准,从问题的初始状态出发,直接去求每一步的最优解,通过若干次的贪心选择,最终得出整个问题的最优解,这种求解方法就是贪心算法。

2.从贪心算法的定义可以看出,贪心算法不是从整体上考虑问题,它所做出的选择只是在某种意义上的局部最优解,而由问题自身的特性决定了该题运用贪心算法可以得到最优解。

3.如果一个问题可以同时用几种方法解决,贪心算法应该是最好的选择之一。

4.贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,希望得到结果是最好或最优的算法。

5.贪心算法是一种能够得到某种度量意义下的最优解的分级处理方法,通过一系列的选择得到一个问题的解,而它所做的每一次选择都是当前状态下某种意义的最好选择。即希望通过问题的局部最优解求出整个问题的最优解。

6.这种策略是一种很简洁的方法,对许多问题它能产生整体最优解,但不能保证总是有效,因为它不是对所有问题都能得到整体最优解。

7.利用贪心策略解题,需要解决两个问题:

(1)该题是否适合于用贪心策略求解;

(2)如何选择贪心标准,以得到问题的最优/较优解。

8.贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。

(1)在动态规划算法中,每步所做的选择往往依赖于相关子问题的解,因而只有在解出相关子问题后,才能做出选择。

(2)在贪心算法中,仅在当前状态下做出最好选择,即局部最优选择,然后再去解出这个选择后产生的相应的子问题。

9.当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。

10.运用贪心策略在每一次转化时都取得了最优解。问题的最优子结构性质是该问题可用贪心算法或动态规划算法求解的关键特征。

11.贪心算法的每一次操作都对结果产生直接影响,而动态规划则不是。

12.贪心算法对每个子问题的解决方案都做出选择,不能回退;动态规划则会根据以前的选择结果对当前进行选择,有回退功能。

13.动态规划主要运用于二维或三维问题,而贪心一般是一维问题。

14.使用贪心算法求解问题应该考虑如下几个方面:

(1)候选集合A:为了构造问题的解决方案,有一个候选集合A作为问题的可能解,即问题的最终解均取自于候选集合A。

(2)解集合S:随着贪心选择的进行,解集合S不断扩展,直到构成满足问题的完整解。

(3)解决函数solution:检查解集合S是否构成问题的完整解。

(4)选择函数select:即贪心策略,这是贪心法的关键,它指出哪个候选对象最有希望构成问题的解,选择函数通常和目标函数有关。

(5)可行函数feasible:检查解集合中加入一个候选对象是否可行,即解集合扩展后是否满足约束条件。

贪心算法的一般流程

//A是问题的输入集合即候选集合
Greedy(A)
{
S={ };           //初始解集合为空集
while (not solution(S))  //集合S没有构成问题的一个解
{
x = select(A);     //在候选集合A中做贪心选择
if feasible(S, x)    //判断集合S中加入x后的解是否可行
S = S+{x};

A = A-{x};
}

return S;

15.原则上来说对于有多种变量的问题来说动态规划能够做出最正,确的解答,但贪心算法对于某些适用于上述规则的问题可以大幅缩短运算。

16.贪心算法是在最短的时间内找出当前一步的最优解,但最终组合到一起时却不一定是最优的。

下面举一个例子

给定一些数组,要求取出的数组相互之间无交且包含的区间范围最大。根据贪心算法,首先按照每个数组的尾部从小到大排序,随后判断下一个数组的头部是否大于当前数组的尾部,若不是的话则这两个数组为符合要求的数组。在数组繁多的情况下这种算法无疑大幅度减少了运算时间,但是一定正确吗?

[200,300],[1,301],[400,500],我们一眼就可以看出,选择后面两个数组包含的区间范围最大,然而根据贪心算法第一个数组可以取到而第二个数组和第一个数组有交因而呗舍弃。当然,这个例子选取的数组较少,若是有很多个数组哪怕最开始选到的数组不是最优的,后面的其他数组反而会因此被选中加入区间中最后得到最大的区间。由此看来,贪心算法依然是解决问题的最快方法,但舍弃了严谨性一味追求greedy(贪婪)却势必在一些时候得到的不是最优的,在时间有限的范围内,贪心算法能找到相对来说最优的结果对我们来说已经足够了。

17.贪心算法不只是一种算法,更是一种思想,让我们看到问题抽丝剥茧看到最里面的一部分,有些像数学函数一样把多个变量都由一个变量的不同函数分别表示,最终只剩下一个变量,贪心算法也是如此。当找到问题的本质时,就能以最短的时间得到最优的结果。

三、回溯算法

1.回溯法是一种组织搜索的一般技术,有“通用的解题法”之称,用它可以系统的搜索一个问题的所有解或任一解。

2.有许多问题,当需要找出它的解集或者要求回答什么解是满足某些约束条件的最佳解时,往往要使用回溯法。

3.回溯可以系统地搜索一个问题的所有解或任意解,既有系统性又有跳跃性。
4.回溯法的基本做法是搜索,或是一种组织得井井有条的,能避免不必要搜索的穷举式搜索法。
5.这种以深度优先的方式系统地搜索问题的解的方法称为回溯法。

6.应用回溯法求解时,需要明确定义问题的解空间。问题的解空间应至少包含问题的一个(最优)解。

7.在生成解空间树时,定义以下几个相关概念:
活结点:如果已生成一个结点而它的所有儿子结点还没有全部生成,则这个结点叫做活结点。

扩展结点:当前正在生成其儿子结点的活结点叫扩展结点(正扩展的结点)。
死结点:不再进一步扩展或者其儿子结点已全部生成的结点就是死结点。
8.在确定了解空间的组织结构后,回溯从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。

这个开始结点成为一个活结点,同时成为当前的扩展结点。在当前的扩展结点,搜索向深度方向进入一个新的结点。这个新结点成为一个新的活结点,并成为当前的扩展结点。

若在当前扩展结点处不能再向深度方向移动,则当前的扩展结点成为死结点,即该活结点成为死结点。此时回溯到最近的一个活结点处,并使得这个活结点成为当前的扩展结点。

回溯法以这样的方式递归搜索整个解空间(树),直至满足中止条件。

9.在回溯法搜索解空间树时,通常采用两种策略(剪枝函数)避免无效搜索以提高回溯法的搜索效率:

用约束函数在扩展结点处减去不满足约束条件的子树。

用限界函数减去不能得到最优解的子树。

10.有时问题是要从一个集合的所有子集中搜索一个集合,作为问题的解。或者从一个集合的排列中搜索一个排列,作为问题的解。

11.回溯算法可以很方便地遍历一个集合的所有子集或者所有排列。

12.当问题是要计算n个元素的子集,以便达到某种优化目标时,可以把这个解空间组织成一棵子集树。

13.遍历子集树的任何算法,其计算时间复杂度都是Ω(2n)。

14.当所给的问题是确定n个元素满足某种性质的排列时,可以把这个解空间组织成一棵排列树。

15.排列树通常有n!个叶子结点。因此遍历排列树时,其计算时间复杂度是Ω(n!) 

16.回溯算法是要在可行解中找最优解。

17.随着深度搜索的进行,子集树的扩展节点不变,排列树的扩展节点变少。

18.对于深度搜索来说,搜索下去就有可能再回到这个节点。

19.对于排列树很多时候需要注意恢复现场。

四、分支限界算法

1.分支限界法是一个用途十分广泛的算法,运用这种算法的技巧性很强,不同类型的问题解法也各不相同。

2.分支限界法的基本思想是对有约束条件的最优化问题的所有可行解(数目有限)空间进行搜索。

(1)该算法在具体执行时,把全部可行的解空间不断分割为越来越小的子集(称为分支),并为每个子集内的解的值计算一个下界或上界(称为限界)。

(2)在每次分支后,对凡是界限超出已知可行解值那些子集不再做进一步分支。这样,解的许多子集(即搜索树上的许多结点)就可以不予考虑,从而缩小了搜索范围。

(3)这一过程一直进行到找出可行解为止,该可行解的值不大于任何子集的界限。

(4)这种算法一般可以求得最优解。

4.在分支限界法中,每一个活结点只有一次机会成为扩展结点。

(1)活结点一旦成为扩展结点,就一次性产生其所有儿子结点。

(2)在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。

(3)从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。

(4)这个过程一直持续到找到所需的解或活结点表为空时为止。

5.从活结点表中选择下一个活结点作为新的扩展结点,分支限界算法通常可以分为两种形式:

(1)FIFO(First In First Out)分支限界算法

按照先进先出(FIFO)原则选择下一个活结点作为扩展结点,即从活结点表中取出结点的顺序与加入结点的顺序相同。

(2)最小耗费或最大收益分支限界算法

在这种情况下,每个结点都有一个耗费或收益。

根据问题的需要,可能是要查找一个具有最小耗费的解,或者是查找一个具有最大收益的解。

6.实现分支限界算法时,首先确定目标值的上下界,边搜索边减掉搜索树的某些分支,提高搜索效率。

7.在搜索时,绝大部分需要用到剪枝。“剪枝”是搜索算法中优化程序的一种基本方法,需要通过设计出合理的判断方法,以决定某一分支的取舍。

8.若我们把搜索的过程看成是对一棵树的遍历,那么剪枝就是将树中的一些“死结点”,不能到达最优解的枝条“剪”掉,以减少搜索的时间。

9.分支限界法类似于回溯法,是一种在问题的解空间树T上搜索问题解的算法。

10.一般情况下,分支限界法与回溯法的求解目标不同。回溯法的求解目标是找出T中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。

五、动态规划

1.动态规划是解决多阶段决策问题的一种方法。

2.多阶段决策问题:如果一类问题的求解过程可以分为若干个互相联系的阶段,在每一个阶段都需作出决策,并影响到下一个阶段的决策。

3.多阶段决策问题,就是要在可以选择的那些策略中间,选取一个最优策略,使在预定的标准下达到最好的效果。

4.最优性原理
(1)不论初始状态和第一步决策是什么,余下的决策相对于前一次决策所产生的新状态,构成一个最优决策序列。

(2)最优决策序列的子序列,一定是局部最优决策子序列。

(3)包含有非局部最优的决策子序列,一定不是最优决策序列。

5.动态规划的指导思想
(1)在做每一步决策时,列出各种可能的局部解.

(2)依据某种判定条件,舍弃那些肯定不能得到最优解的局部解。

(3)以每一步都是最优的来保证全局是最优的。

6.动态规划的几个概念

阶段:据空间顺序或时间顺序对问题的求解划分阶段。

状态:描述事物的性质,不同事物有不同的性质,因而用不同的状态来刻画。对问题的求解状态的描述是分阶段的。

决策:根据题意要求,对每个阶段所做出的某种选择性操作。

状态转移方程:用数学公式描述与阶段相关的状态间的演变规律。

7.大多数问题动态规划都可以解决,但是太慢。因此有些问题贪心确实比动态快的多。不过相对的贪心解决问题并不如动态规划精细,得出来的不一定是最优解,只能说是相对最优,根据情况选择不同的算法解决问题才是王道。

posted @ 2018-11-01 22:31  你的雷哥  阅读(950)  评论(0编辑  收藏  举报