算法引论
算法与程序
通俗地讲, 算法 是指解决问题的方法或过程。严格地讲,算法是满足下述性质的指令序列:
- 输入:有零个或多个外部量作为算法的输入。
- 输出:算法产生至少一个量作为输出。
- 确定性:组成算法的每条指令是清晰度、无歧义的。
- 有限性:算法中每条指令的执行次数有限,执行每条指令的时间也有限。
而 程序 与算法不同, 程序是算法用某种程序设计语言的具体实现。
程序可以不满足算法的性质(4)即有限性,例如操作系统,它是在无限循环中执行的程序。然而,可把操作系统的各种任务看成一些单独的问题,每一个问题由操作系统中的一个子程序通过特定的算法实现,该子程序得到输出结果后便停止。
表达算法的抽象机制
对于一个明确的数学问题,设计它的算法,总是先选用该问题的一个数据模型。
接着弄清楚该问题数据模型在已知条件下的初始状态和要求的结果状态,以及这两个状态之间的隐含关系。
然后探索从数据模型的已知初始状态到达要求的结果状态所需的运算步骤(这些运算步骤就是求解该问题的算法)。
算法复杂性分析
算法复杂性的高低体现在运行该算法所需要的计算机资源的多少上,所需要的资源越多,该算法的复杂性越高,反之就越低。
而计算机中,最重要的资源就是时间和空间(即存储器),因此,算法的复杂性有 时间复杂性 和 空间复杂性 之分。 对于任意一个给定的问题,设计出复杂性尽可能低的算法是在设计算法时追求的重要目标。
但是,在有些情况下,复杂性的追求也不完全是越低越好,比如:可读性、可扩展等也很重要,需要结合实际情况选择,切莫陷入“虚无的理想主义者”陷阱中去。
递归与分治策略
直接或间接地调用自身的算法称为递归算法。
那什么是分治策略?
有时候,一个问题的规模比较大,也比较难,将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之,便是分治策略。
比如对100个数进行排序要比对2个数进行排序要困难。
而分治往往产生许多原问题的较小规模的子问题,自然就导致递归算法。因此,分治与递归总是如影随形的出现(当然,有时候我们会出于一些原因的考虑想办法消除递归)。
动态规划
基本的思想也是将待解问题分解称若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
但与分治法不同的是,子问题往往不是互相独立的,可能存在需要重复求解的子问题,这点非常重要。由此,我们在求解一个子问题的时候会记录其解,下次遇到类似的问题,先去查一下表,如果已经有了解,直接使用,否则再计算后进行登记。
通常可以按以下步骤设计算法:
- 找出最优解的性质,并刻画其结构性质。
- 递归地定义最优值。
- 以自底向上的方式计算出最优值。
- 根据计算最优值时得到的信息,构造最优解。
可以看出来,求解的规模是从小到大进行的,这意味着所有的子问题都会被求解一遍。而在有时候,比如我们可能只是需要一个解,那也可以考虑从大到小进行,也就是 备忘录算法 。
一般来说,如果一个问题的所有子问题都至少要解一次时,用动态规划比备忘录要好,因为此时动态规划没有任何多余的计算。当子问题中部分可不必求解时,用备忘录则更好。
贪心算法
算法的主体思路就是每次做的选择都是当前状态下局部最好的选择,这种策略并不总是能获得最优解。
怎么知道是否可以用贪心算法解此问题,以及能否得到问题的最优解呢?这类问题一般都具有两个重要的性质: 贪心选择性质 和 最优子结构性质 。
贪心选择性质
也就是所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。
最优子结构性质
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
回溯法
说的直白点就是深度优先方式系统搜索问题的算法。
它在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,先判断该结点是否包含问题的解,如果肯定不包含,则跳过对以该结点为根的子树的搜索,逐层向其祖先结点回溯,否则,进入该子树,继续按深度优先策略搜索。
回溯法求问题的所有解时,要回溯到根,且根结点多所有子树都被搜索遍才结束,而在求一个解时,只有搜索到问题的一个解就可结束。
分支限界法
对比回溯法就很容易思考,用广度优先的办法,不断扩大当前节点的孩子为当前节点,主要是求解一个最优解,算法相比回溯法要简单些。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。