代码改变世界

算法思想

2014-04-23 13:53  Loull  阅读(547)  评论(0编辑  收藏  举报

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

几种算法思想

基础算法思想

算法洗脑系列