算法概述及五大常用算法

算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。

也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。

如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。

不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。

算法中的指令描述的是一个计算,当其运行时能从一个初始状态和(可能为空的)初始输入开始,经过一系列有限而清晰定义的状态,最终产生输出并停止于一个终态。一个状态到另一个状态的转移不一定是确定的。

随机化算法在内的一些算法,包含了一些随机输入。

算法的特征

  1. 有穷性(Finiteness)

    算法必须能在执行有限个步骤之后终止;

  2. 确切性(Definiteness)

    算法的每一步骤必须有确切的定义;

  3. 输入项(Input)

    一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定出了初始条件;

  4. 输出项(Output)

    一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;

  5. 可行性(Effectiveness)

    亦称有效性,是指算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步,即每个计算步都可以在有限时间内完成。

算法的要素

  • 数据对象的运算和操作

    计算机可以执行的基本操作是以指令的形式描述的。

    一个计算机系统能执行的所有指令的集合,成为该计算机系统的指令系统。一个计算机的基本运算和操作有如下四类:

    1. 算术运算:加减乘除等运算
    2. 逻辑运算:或、且、非等运算
    3. 关系运算:大于、小于、等于、不等于等运算
    4. 数据传输:输入、输出、赋值等运算
  • 算法的控制结构

    一个算法的功能结构不仅取决于所选用的操作,而且还与各操作之间的执行顺序有关。

五大常用算法

分治

概念

字面上的解释是“分而治之”,就是 把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并 。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换……

任何一个可以用计算机求解的问题所需的计算时间都与其规模有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如:对于n个元素的排序问题,当n=1时,不需任何计算。n=2时,只要作一次比较即可排好序。n=3时只要作3次比较即可,…。而当n较大时,问题就不那么容易处理了。要想直接解决一个规模较大的问题,有时是相当困难的。

策略

对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解 。这种算法设计策略叫做分治法。

如果原问题可分割成k个子问题,1 < k ≤ n, 且这些子问题都可解并可利用这些子问题的解求出原问题的解,那么这种分治法就是可行的。由分治法产生的子问题往往是原问题的较小模式,这就为使用递归技术提供了方便。在这种情况下,反复应用分治手段,可以使子问题与原问题类型一致而其规模却不断缩小,最终使子问题缩小到很容易直接求出其解。 这自然导致递归过程的产生。分治与递归像一对孪生兄弟,经常同时应用在算法设计之 中,并由此产生许多高效算法。

适用条件

  1. 该问题的规模缩小到一定的程度就可以容易地解决;
  2. 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;
  3. 利用该问题分解出的子问题的解可以合并为该问题的解;
  4. 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。

第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;

第二条特征是应用分治法的前提,它也是大多数问题可以满足的,此特征反映了递归思想的应用;

第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。

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

步骤

  1. 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
  2. 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
  3. 合并:将各个子问题的解合并为原问题的解。

例子

二分归并排序、快速排序、幂乘算法、矩阵算法、平面点对、棋盘覆盖、汉诺塔等

动态规划

概念

每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。

策略

与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。

由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中。

与分治法最大的差别:适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的。即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解。

适用条件

  1. 最优子结构性质

    一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。

  2. 无后效性

    将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态

  3. 子问题重叠

    即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。

    该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势。

设计步骤

  1. 分析最优解的性质,并刻画其结构特征。
  2. 递归的定义最优解。
  3. 以自底向上或自顶向下的记忆化方式(备忘录法)计算出最优值
  4. 根据计算最优值时得到的信息,构造问题的最优解

例子

矩阵链相乘、投资问题、背包问题、最大字段和问题、最长公共子序列问题、图像压缩问题、多边形游戏

贪心

概念

在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。

贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。

所以对所采用的贪心策略一定要仔细分析其是否满足无后效性。

适用条件

贪心策略适用的前提是:局部最优策略能导致产生全局最优解。

实际上,贪心算法适用的情况很少。一般,对一个问题分析是否适用于贪心算法,可以先选择该问题下的几个实际数据进行分析,就可做出判断。

步骤

  1. 建立数学模型来描述问题。
  2. 把求解的问题分成若干个子问题。
  3. 对每一子问题求解,得到子问题的局部最优解。
  4. 把子问题的解局部最优解合成原来解问题的一个解

复杂度分析

通常是一轮处理(一般是一层for循环就解决了),一般是O(n)+O(nlogn)(排序消耗)的时间

例子

活动选择问题、最优装载问题、最小延迟调度问题、最优前缀码、最小生成树、单源最短路径问题

回溯

此算法实际上是一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。

回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。

基本思想

在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。

若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。

而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。

适用条件

满足多米诺性质(必要条件),是一个多部判断求解,适用于搜索和优化问题

步骤

  1. 针对所给问题,确定问题的解空间

    首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个最优解。

  2. 确定结点的扩展搜索规则

  3. 以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。

复杂度分析

此算法的复杂度往往和蛮力算法没有太大的差别,但是它可以做到更好的剪枝效果,比蛮力算法更能快速搜索

例子

n后问题、0-1背包问题、货郎问题、最优装载问题、圆排列问题、最大团问题

分支限界

类似于回溯法,也是一种在问题的解空间树T上搜索问题解的算法。但在一般情况下,分支限界法与回溯法的求解目标不同。回溯法的求解目标是找出T中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。

  • 分支搜索算法

    所谓“分支”就是采用广度优先的策略,依次搜索E-结点的所有分支,也就是所有相邻结点,抛弃不满足约束条件的结点,其余结点加入活结点表。然后从表中选择一个结点作为下一个E-结点,继续搜索。

    选择下一个E-结点的方式不同,则会有几种不同的分支搜索方式。

    1. FIFO搜索
    2. LIFO搜索
    3. 优先队列式搜索
  • 分支限界搜索算法

适用条件

以广度优先或以最小耗费(最大效益)优先的方式搜索问题

两种方法

  1. 队列式(FIFO)分支限界法
    按照队列先进先出(FIFO)原则选取下一个节点为扩展节点。

  2. 优先队列式分支限界法
    按照优先队列中规定的优先级选取优先级最高的节点成为当前扩展节点。

搜索策略

在当前节点(扩展节点)处,先生成其所有的子节点(分支),然后再从当前的活节点(当前节点的子节点)表中选择下一个扩展节点。

为了有效地选择下一个扩展节点,加速搜索的进程,在每一个活节点处,计算一个函数值(限界),并根据函数值,从当前活节点表中选择一个最有利的节点作为扩展节点,使搜索朝着解空间上有最优解的分支推进,以便尽快地找出一个最优解。

分支限界法解决了大量离散最优化的问题。

设计步骤

确定队列的入队和出队以及在什么时候入队和出队、确定活结点的属性、确定最优解存储、确定约束条件及代价函数

例子

布线问题、0-1背包问题、最大团问题、货郎问题、最优装载问题、批作业调度问题