穷举算法基础
穷举法,亦称作分类证明、分类分析证明、完全归纳法或暴力法, 是一种数学证明方法, 它将所求证的命题分为有限种情形或是等价情形的集合, 然后就每种类型分别检验该命题是否成立.这是一种直接证明(英语:direct proof)法. 穷举法证明包括两阶段:
证明分类是完全的, 也就是说每一个待证的个例皆符合(至少)一类情形的条件;
分别对每一类情形给出证明.
数字计算机的普及大大提升了穷举法的易用性.
计算机专家系统可以解答出很多被问到的问题. 理论上来说, 穷举法适用于任何有限情形. 然而, 由于数学上大部分集合是无限的, 此法鲜少能够用以导出一般的数学结论.
分析的角度
用穷举算法解决问题,通常可以从两个方面进行分析
问题所涉及的情况:问题所涉及的情况有哪些,情况的种数可不可以确定。把它描述出来。应用穷举时对问题所涉及的有限种情形必须一一列举,既不能重复,也不能遗漏。重复列举直接引发增解,影响解的准确性;而列举的遗漏可能导致问题解的遗漏。
答案需要满足的条件:分析出来的这些情况,需要满足什么条件,才成为问题的答案。把这些条件描述出来。
只要把这两个方面分析好了,问题自然会迎刃而解。
穷举通常应用循环结构来实现。
在循环体中,根据所求解的具体条件,应用选择结构实施判断筛选,求得所要求的解。
例子
鸡兔同笼问题最早记载于1500年前的《孙子算经》,这是我国古代一一个非常有名的问题。鸡兔同笼的原文如下:今有鸡兔同笼,上有三十五头,下有九十四足,问鸡兔各几何?这个问题的大致意思是:在一个笼子里关着若干只鸡和若干只兔,从上面数共有35个头;从下面数有94只脚。问笼中鸡和兔的数量各是多少?
递推算法思想
一、递推算法所谓递推,是指从已知的初始条件出发,依据某种递推关系,逐次推出所要求的各中间结果及最后结果。其中初始条件或是问题本身已经给定,或是通过对问题的分析与化简后确定。
从已知条件出发逐步推到问题结果,此种方法叫顺推。
从问题出发逐步推到已知条件,此种方法叫逆推。
无论顺推还是逆推,其关键是要找到递推式。
这种处理问题的方法能使复杂运算化为若干步重复的简单运算,充分发挥出计算机擅长于重复处理的特点。
递推法是一种重要的数学方法,在数学的各个领域中都有广泛的运用,也是计算机用于数值计算的一个重要算法。
递推算法的首要问题是得到相邻的数据项间的关系(即递推关系)。
递推算法避开了求通项公式的麻烦,把一个复杂的问题的求解,分解成了连续的若干步简单运算。一般说来,可以将递推算法看成是一种特殊的迭代算法。
二、递推算法是一种理性思维模式的代表,其根据已有的数据和关系,逐步推导而得到结果。递推算法的执行过程如下:
根据已知结果和关系,求解中间结果。
判定是否达到要求,如果没有达到,则继续根据已知结果和关系求解中间结果。如果满足要求,则表示寻找到一个正确的答案。
三、例子
数学里面的斐波那契数列便是一个使用递推算法的经典例子。13世纪意大利数学家斐波那契的《算盘书》中记载了典型的兔子产仔问题,其大意如下:如果一对两个月大的兔子以后每-一个月都可以生一对小兔子,而一对新生的兔子出生两个月后才可以生小兔子。也就是说,1月份出生,3月份才可产仔。那么假定一年内没有产生兔子死亡事件,那么1年后共有多少对兔子呢?
分析:
我们先来分析一下兔子产仔问题。我们来逐月看一次每月的兔子对数:
第一个月: 1对兔子;
第二个月: 1对兔子;
第三个月: 2对兔子;
第四个月: 3对兔子;
第五个月: 5对兔子;
从上面可以看出,从第个3月开始,每个月的兔子总对数等于前两个月兔子数的总和。相应的计算公式如下:
第n个月兔子总数F=Fn-1+Fn-2
这里,初始第一个月的兔子数为F1=1,第二个月的兔子数为F2=1.
递归算法思想
一种便于理解的心理模型,是认为递归定义对对象的定义是按照“先前定义的”同类对象来定义的。
例如:你怎样才能移动100个箱子?答案:你首先移动一个箱子,并记下它移动到的位置,然后再去解决较小的问题:你怎样才能移动99个箱子?最终,你的问题将变为怎样移动一个箱子,而这时你已经知道该怎么做的。
递推:从初值出发反复进行某一运算得到所需结果。-----从已知到未知,从小到达(比如每年长高9cm,20年180,30后270)
递归:从所需结果出发不断回溯前一运算直到回到初值再递推得到所需结果----从未知到已知,从大到小,再从小到大
分治算法思想
把一个复杂的问题分成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
这个技巧是很多高效算法的基础,如排序算法(归并排序、快速排序)、傅立叶变换(快速傅立叶变换)。
实现
在每一层递归上都有三个步骤:
分解:将原问题分解为若干个规模较小,相对独立,与原问题形式相同的子问题。
解决:若子问题规模较小且易于解决时,则直接解。否则,递归地解决各子问题。
合并:将各子问题的解合并为原问题的解
贪心算法思想
贪心算法,又名贪婪法,是寻找最优解问题的常用方法,
这种方法模式一般将求解过程分成若干个步骤,但每个步骤都应用贪心原则,选取当前状态下最好/最优的选择(局部最有利的选择),并以此希望最后堆叠出的结果也是最好/最优的解。
(看着这个名字,贪心,贪婪这两字的内在含义最为关键。这就好像一个贪婪的人,他事事都想要眼前看到最好的那个,看不到长远的东西,也不为最终的结果和将来着想,贪图眼前局部的利益最大化,有点走一步看一步的感觉)
贪婪法的基本步骤:
步骤1:从某个初始解出发;
步骤2:采用迭代的过程,当可以向目标前进一步时,就根据局部最优策略,得到一部分解,缩小问题规模;
步骤3:将所有解综合起来。
事例一:找零钱问题
假设你开了间小店,不能电子支付,钱柜里的货币只有 25 分、10 分、5 分和 1 分四种硬币,如果你是售货员且要找给客户 41 分钱的硬币,如何安排才能找给客人的钱既正确且硬币的个数又最少?
这里需要明确的几个点:
货币只有 25 分、10 分、5 分和 1 分四种硬币;
找给客户 41 分钱的硬币;
硬币最少化
思考,能使用我们今天学到的贪婪算法吗?怎么做?
回顾一下上文贪婪法的基本步骤,1,2,3
1.找给顾客sum_money=41分钱,可选择的是25 分、10 分、5 分和 1 分四种硬币。能找25分的,不找10分的原则,初次先找给顾客25分;
2.还差顾客sum_money=41-25=16。然后从25 分、10 分、5 分和 1 分四种硬币选取局部最优的给顾客,也就是选10分的,此时sum_money=16-10=6。重复迭代过程,还需要sum_money=6-5=1,sum_money=1-1=0。至此,顾客收到零钱,交易结束;
3.此时41分,分成了1个25,1个10,1个5,1个1,共四枚硬币。
回溯算法思想
回溯的思想是这样的:
假设你在走迷宫,现在你站在一个分岔口,你不知道该向哪里走,所以你随机选择一个路口向前走,当你走到死胡同(或者已经走过的路)的时候,你知道这条路不对,于是你退回到上一个路口,选择另一条路再走。这样往复,直到找到出口。
适合使用回溯的问题
回溯算法适合解决这样一类问题:
在一组数据中,我们有一个期望得到的解,在求解的过程中,我们可以有规律的枚举所有的解,过程中通过不断地回溯、剪枝,最终得到一个与条件最接近的解。
你能发现,回溯算法的应用场景和贪婪算法的应用场景很像,但是贪婪算法并不一定能求得最优解,而回溯虽然要枚举的量很大,但是可以找到最接近期待的解。
回溯的例子
八皇后(n皇后)问题
八皇后是回溯算法的招牌案例,它的描述如下:
我们有一个 8x8 的棋盘,希望往里放 8 个棋子(皇后),每个棋子所在的行、列、对角线都不能有另一个棋子。你可以看我画的图,第一幅图是满足条件的一种方法,第二幅图是不满足条件的。八皇后问题就是期望找到所有满足这种要求的放棋子方式。
模拟算法思想
模拟退火来自冶金学的专有名词退火。
退火是将材料加热后再经特定速率冷却,目的是增大晶粒的体积,并且减少晶格中的缺陷。材料中的原子原来会停留在使内能有局部最小值的位置,加热使能量变大,原子会离开原来位置,而随机在其他位置中移动。
退火冷却时速度较慢,使得原子有较多可能可以找到内能比原先更低的位置。
使用场景
模拟退火算法主要用于难以准确求出具体的解的问题之中。
通过多次迭代,它可以不断地接近最优解。
初始化
由一个产生函数从当前解产生一个位于解空间的新解,并定义一个足够大的数值作为初始温度。
迭代过程
迭代过程是模拟退火算法的核心步骤,分为新解的产生和接受新解两部分:
1. 由一个产生函数从当前解产生一个位于解空间的新解;为便于后续的计算和接受,减少算法耗时,通常选择由当前新解经过简单地变换即可产生新解的方法,如对构成新解的全部或部分元素进行置换、互换等,注意到产生新解的变换方法决定了当前新解的邻域结构,因而对冷却进度表的选取有一定的影响。
2. 计算与新解所对应的目标函数差。因为目标函数差仅由变换部分产生,所以目标函数差的计算最好按增量计算。事实表明,对大多数应用而言,这是计算目标函数差的最快方法。
3. 判断新解是否被接受,判断的依据是一个接受准则,最常用的接受准则是Metropolis准则:若Δt′<0则接受S′作为新的当前解S,否则以概率exp(-Δt′/T)接受S′作为新的当前解S。
4. 当新解被确定接受时,用新解代替当前解,这只需将当前解中对应于产生新解时的变换部分予以实现,同时修正目标函数值即可。此时,当前解实现了一次迭代。可在此基础上开始下一轮试验。而当新解被判定为舍弃时,则在原当前解的基础上继续下一轮试验。
模拟退火算法与初始值无关,算法求得的解与初始解状态S(是算法迭代的起点)无关;模拟退火算法具有渐近收敛性,已在理论上被证明是一种以概率1收敛于全局最优解的全局优化算法;模拟退火算法具有并行性。
停止准则
迭代过程的停止准则:温度T降至某最低值时,完成给定数量迭代中无法接受新解,停止迭代,接受当前寻找的最优解为最终解。
退火方案
在某个温度状态T下,当一定数量的迭代操作完成后,降低温度T,在新的温度状态下执行下一个批次的迭代操作。
伪代码
寻找能量 E(s) 最低的状态 s
// 设定目前状态为s0,其能量E(s0)s := s0; e := E(s) // 评估次数kk := 0 // 如果还有时间(评估次数还不到kmax)并且结果不够好(能量e不够低)则:while k < kmax and e > emin // 随机选取一个邻近状态sn sn := neighbour(s) // sn的能量为E(sn) en := E(sn) // 决定是否移到邻近状态sn if random() < P(e, en, temp(k/kmax)) // 移动到邻近状态sn s := sn; e := en // 评估完成,次数k加一 k := k + 1 // 返回状态return s