常用算法设计技术总结

算法,即计算的方法,使用计算的思想、方法、工具和技术来实现问题求解的思路和途径。计算机提供了计算的能力和硬件设施;算法则提供了计算的思想和软件技术,更好地发挥计算机的潜能。

—— 引

有人说,算法无用,这种观点就如同盲人看不到花开就说世界上没有花一样,是一个长眼睛的人无论如何也难以接受的。举个简单的例子,假如你要对1000,000条记录进行排序,你的计算机每秒可处理1000,000 条记录, 那么, 如果你选择 O(nlogn)的快速排序,所花时间大约是 log2(1000000) = 20s, 如果你选择O(n^2)的插入排序,所花时间大约是 1000000^2/1000000 = 1000000s = 11.57天;如果你选择O(n^3)的XX排序,所花时间大约是1000000^2 = 31709.791年。很明显,拥有子弹的不如拥有导弹的,拥有导弹的不如拥有核弹的。你很容易就可以知道,谁才是未来世界的王者。

算法大师Knuth曾说过:“……算法作为一种一般的智能性工具,有助于对各学科知识的理解……只有将知识教给计算机,才能真正理解知识……比起常规的问题理解来说,将知识表述成算法的形式将使理解更为深刻……”所以,算法不仅可以决定统治世界的王者(除非你将世界上的所有计算机全部销毁),还可以有助于个人的知识学习和理解,甚至,对生活的领悟;算法有助于高效工作的学习工作。

现在,就让我们来梳理一下常用的算法设计技术吧!

一、 分治法:

将问题实例 P(n) 分解为 b 个子问题实例 P(n/b) , 其中有 a 个子问题需要求解。 然后将子问题的解合并起来得到原问题的解。

一言以蔽之, 即“分而合之”。

例子:

①   归并排序: 将待排序列表先分成若干的子列表,分别排序,然后合并起来。归并排序 的关键在于“合”的过程;

②   快速排序: 选取列表中的某种元素,放在列表中的某个位置,使列表分为 小于该元素 和 不小于该元素 的两个子列表。然后, 分别对两个子列表重复上述步骤,直到整个列表有序为止。 快速排序法 关键在“分”的过程。

③    二叉树遍历: 分别遍历根结点、左子树和右子树。具有天然的分治和递归特性。

分治法的效率:

T(n) = aT(n/b) + f(n) , 其中  f(n) 是“分”或者“合”的过程中所耗费的时间。对于归并排序来说, f(n) 是将两个有序列表合并成一个更大的有序列表所耗费的时间; 对于快速排序来说, f(n) 是选取分割元素并将其放在最终位置所耗费的时间。

根据主定理: 令 f(n) = O(n^d),

① 当 d > log(a)/log(b) 时, T(n) = n^d; ② 当 d = log(a)/log(b) 时, T(n) = (n^d)log(n)

③ 当 d < log(a)/log(b) 时, T(n) = n^(log(a)/log(b))

主定理其实很好记: T(n) 的效率 由 aT(n/b) 和 f(n) 的效率比较而得, 其中 aT(n/b) 的效率可看成: n^(log(a)/log(b)),

只要比较幂数取大者就可以了。

常用的分治法,是将问题实例分成规模大致相等的2个子问题实例。根据主定理:

T(n) = 2T(n/2) + O(C) , C 是常数 --------- T(n) = O(n)

T(n) = 2T(n/2) + n   -----  T(n) = O(nlog2(n))

T(n) = 2T(n/2) + n^2 ------------------- T(n) = O(n^2)

可以看出, f(n) 也是起很重要作用的, f(n) 不能太大, 最好是线性的,否则, 采用分治法的效率就可能不高了。合理采用分治法的效率一般应该可以达到O(nlog2(n)),至少应该比采取其他算法技术的效率要高一个级别。

二、减治法:

当 a = 1 时, 即 T(n) = T(n/b) + f(n) 时, 分治法就退化为减治法。 此时,f(n) 的大小就起非常重要的影响了。

减治法有三种形式:

① 减常量: T(n) = T(n-b) + f(n)  eg.

T(n) = T(n-1) + C, C为常数 -----  T(n) = O(n)

T(n) = T(n-1) + n  -----------  T(n) = O(n^2)

减常量的例子: 插入排序 -----  T(n) = T(n-1) + n   T(n) = O(n^2)

② 减常因子: T(n) = T(n/b) + f(n), eg.

T(n) = T(n/2) + C, C为常数 ------ T(n) = O(log2(n))   折半查找

T(n) = T(n/2) + n ---------------- T(n) = O(n^2)

③ 减可变规模: 例如 求最大公约数 gcd(m,n) = gcd(n, m%n)

总之,在使用分治法或者减治法时,应当合理地进行子问题分割,尽量使分和合的过程简单, 使 f(n) 保持线性,这样,才能达到分治法和减治法的高效。

三、动态规划法

动态规划法的解通常具有如下递推形式: S(n) = G(S(n-i) , f(n)) i = 1,2,……,n 其中  S(n-i) 和 S(n-j) 往往具有重合的子问题  S(n-k), k>i,k>j,此时,可以采用自底向上的迭代法或基于记忆功能的递归法来一步步地构造解S(1),S(2),……,S(n)。由于避免了对子问题的重复求解,因此,动态规划法显示出了比较高的效率。

动态规划法与分治法的区别: 分治法所分成的子问题实例通常没有重合的子问题实例,而动态规划法所分成的子问题实例通常有重合的地方,而且,动态规划法的最优解往往蕴含着子问题实例的最优解,因此,可以由子问题实例的最优解逐步构造最终的最优解。

动态规划法的典型例子: 求斐波那契数列、求二项式系数、背包问题、最长子序列问题、矩阵连乘问题。

四、变治法

变治法基于问题转化的思想,将所要求解的问题实例转化为更为特殊形式的问题实例,或者已被求解的其它问题实例。变治法要求对问题之间的联系具备非凡的洞察力。

典型的变治法包括预排序。将待处理列表先进行排序,然后再对有序列表进行其它处理。比如查找问题, 如果采用顺序查找,则需要O(n)的时间,而先排序后二分查找,则需要至少O(nlog2(n)),显然,预排序似乎是多余的。不过,如果要进行多次查找,则预排序就开始起作用了。假设要对1000条记录进行 C 次查找, 则有 Cn > nlog2(n) + Clog2(n), n=1000 可解得 C > 10. 也就是说,仅仅要查找十次以上,预排序就开始起作用了。

另一个变治法的简单例子是求解最小公倍数lcm(m,n)。如果你知道 lcm(m,n) = m*n/gcd(m,n) , gcd(m,n) 是最大公约数, 有一个非常高效的欧几里得算法,那么,这个问题就显得容易得多了。前提是,你要能够具备丰富的知识和良好的洞察力。该问题的最简单的算法是,从1开始,1,2,……,n, 逐个计算直到能被m和n整除为止。 效率为 O(mn); 较好的算法是,取其中最大数 max = max(m,n), 从1开始,逐个进行相乘,max, 2max,……,kmax, 直到能被n 整除位置,效率为 O(min(m,n)).

采用变治法往往可以收到意想不到的效果。要善于进行问题分析,转化问题。

五、 时空权衡

时空权衡基于“空间充裕廉价,时间宝贵”以及“空间极其有限,时间要求不高”的思想,要么以牺牲空间的代价换取时间的高效,要么以牺牲时间的代价换取空间的要求。前者在桌面PC领域更加常见,后者在单片机、嵌入式等产品中体现较明显。

典型的例子:

计数排序: 使用一个数组来存储比元素大或者小的元素个数,根据这些值来确定元素在最终列表中的位置。

散列法:    使用数组和链表的结合来实现字典的高效。首先,采用某种哈希函数hash(x) 将 基于键key 的记录映射到哈希表的相应位置hashtable[hash(key)]中,如果有冲突(多个键的记录被映射到同一个下标),则使用附在该下标的链表存储冲突的记录。这也是一个显示数据结构组合应用能够产生强大功能的令人深刻的例子。

递归/非递归遍历: 使用额外的栈或者标记来存储遍历过程中已经遍历了的元素,从而避免重复遍历。

六、 贪婪技术

总是从当前的可行选择中选取局部最优的部分解,一步步构造最终的解。贪婪技术逐步构造最优解的三个条件:

1. 可行:满足问题的约束条件

2. 局部最优:在当前可行的选择中总是选择局部最优的部分解。

3. 不可撤销:一旦做出选择,就不可撤销。

贪婪技术的典型例子: 找硬币、最小生成树的 Prim算法 和 Kruskal算法 , 单起点最短路径的 Dijskra算法。

七、 蛮力法

不要小看蛮力法。蛮力法总是可以找到一个解,虽然这个解通常并不实用。蛮力法的作用是,找到一个解,然后对其进行改进。蛮力法有时可以给人带来惊喜。比如 子字符串匹配(判断字符串str 中是否含有子字符串pattern) 的蛮力法版本,其最差效率O(mn),平均效率为O(m), m,n  分别是 str 和 pattern 的长度。也就是说,蛮力法版本的子字符串匹配算法是比较实用的。

蛮力法的典型例子: 排列组合问题的穷举查找法。

八、 迭代改进

迭代改进法,总是最先找到一个可行解,然后通过一些小的、局部的变化来改进这个可行解,使之趋向最优解。迭代改进的关键是稳定性、收敛性。

迭代法的典型例子: 遗传算法

九、 回溯法

回溯法是穷举查找法的改进版本,穷举查找法通常先列举出所有的候选解,然后根据目标问题来找出满足要求的解或最优解。而回溯法则“聪明”了一些,一步一步地构造解的分量,一旦有分量不可行,则此路不通,马上转道。回溯法通常根据问题构造一棵状态树,通过遍历状态树的结点来求解。叶子节点代表无解或可行解。

回溯法的典型例子: n 皇后问题,图的深度遍历

十、 分枝定界技术

分枝定界技术与回溯法非常相似。由于我还没有学到这里,因此,暂不作讨论。分枝定界技术常用来求解最优化问题。

十一、 求近似解

退而求其次,不失为一种明智的选择。如果花20分钟可以获得旅行商问题的实用解,那么,再多花十个小时,使解获得0.1%的改进,是否有必要?对于理论工作者来说,确实如此。但如果是实践工作者,我会选择前者。要学会权衡投资/收益比。

结语:

总结算法,应用算法。算法,当之无愧是计算机科学的灵魂和基石。无论是硬件还是软件,无论是专业还是生活,算法无处不在。不能因为暂时用不到就贬低它的价值,——事实上,这是一种掩耳盗铃的做法。你不用,别人总会用的,而且,别人可以从中体验乐趣,获取丰厚的回报。千万不要跟算法过不去。

参考书籍:

1. 《算法设计与分析基础》[第2版] ,  (美)Anany Levitin , 潘彦译, 2004/06, 清华大学出版社

2. 《算法导论》[第2版],Thomas H. Cormen,Charles E. Leiserson,Ronald L. Rivest,Clifford Stein, 潘金贵 等译, 2006/09, 机械工业出版社

时间: 2024-08-25 07:45:50

常用算法设计技术总结的相关文章

【转载】算法设计之五大常用算法设计方法总结

转载自http://blog.csdn.net/zolalad/article/details/11393915 算法设计之五大常用算法设计方法总结 一.[分治法]  在计算机科学中,分治法是一种很重要的算法.字面上的解释是"分而治之",就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题--直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并.这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)--等.任

《编程珠玑》阅读小记(6) — 算法设计技术

本章简述 通过前面第二章节的叙述,描述了算法设计对程序员的日常影响:算法上的灵机一动可以使程序更加简单.但是本章内容将会发现算法设计的一个不那么常见但更富于戏剧性的贡献:复杂深奥的算法有时可以极大地提高程序性能. 问题及简单算法 本章引入的问题来自一维的模式识别,问题的输入是具有n个浮点数的向量x,输出是输入向量的任何连续子向量中的最大和. 例如,如果输入向量包含以下 N = 10 个元素: arr[N] = { 31, -41, 59, 26, -53, 58, 97, -93, -23, 8

uva 1608 不无聊的序列(附带常用算法设计和优化策略总结)

uva 1608 不无聊的序列(附带常用算法设计和优化策略总结) 紫书上有这样一道题: 如果一个序列的任意连续子序列中都至少有一个只出现一次的元素,则称这个序列时不无聊的.输入一个n个元素的序列,判断它是不是无聊的序列.n<=200000. 首先,在整个序列中找到只出现一次的元素ai.如果不能找到,那它就是无聊的.不然,就可以退出当前循环,递归判断[1, i-1]和[i+1, n]是不是无聊的序列.然而怎么找ai很重要.如果从一头开始找,那么最差情况下的时间复杂度就是O(n^2)的.而如果从两头

常用算法设计和优化策略(本蒟蒻不定期更新)

常用算法设计和优化策略(本蒟蒻不定期更新) 下面是紫书上讲的常用算法设计策略和优化策略: 分治法:将问题分成相同的独立子问题求解.拆分出的问题必须有最优子结构性质(子问题求出的是最优解) 动态规划.本质是:对于一个问题,通过划分阶段,定义状态与状态间的关系,来分解问题.利用单阶段问题之间的联系,或者同一阶段状态之间的联系,一个一个阶段往下决策,最终解决问题. 拆分出的问题必须满足最优子结构性质和无后效性(当前阶段以前的状态不会影响以后的状态,只与当前阶段有关).动归的目的是避免重叠子问题.递推和

[第8章]算法设计技术——一维模式识别

第八章主要介绍了一维模式识别的算法改进过程.问题输入:n个浮点数的向量x. 问题输出:任何连续子向量中的最大和. (1)首先从最直观的做法入手,就是枚举所有情况下的子向量的和,最后比较出最大的和.首先,利用两层for循环,列出了所有可能的子向量,在用一层for循环,求出子向量的和.运算时间为O(n^3). int MaxNum1(int* a,int _len) { int maxNum=0; for(int i=0;i<_len;i++) { for(int j=i;j<_len;j++)

五大常用算法 之 动态规划法

一.基本概念 动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移.一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划. 动态规划是运筹学中用于求解决策过程中的最优化数学方法.当然,我们在这里关注的是作为一种算法设计技术,作为一种使用多阶段决策过程最优的通用方法.它是应用数学中用于解决某类最优化问题的重要工具. 如果问题是由交叠的子问题所构成,我们就可以用动态规划技术来解决它,一般来说,这样的子问题出现在对给定问题求解的递推关系中,这个递推关

算法设计方法:递归的内涵与经典应用

摘要: 大师 L. Peter Deutsch 说过:To Iterate is Human, to Recurse, Divine.中文译为:人理解迭代,神理解递归.毋庸置疑地,递归确实是一个奇妙的思维方式.对一些简单的递归问题,我们总是惊叹于递归描述问题的能力和编写代码的简洁,但要想真正领悟递归的精髓.灵活地运用递归思想来解决问题却并不是一件容易的事情.本文剖析了递归的思想内涵,分析了递归与循环的联系与区别,给出了递归的应用场景和一些典型应用,并利用递归和非递归的方式解决了包括阶乘.斐波那契

算法设计--求连续子向量的最大和问题--论想法思路的重要性

向量[31,-41,59,26,-53,58,97,-93,-23,84] 算法一:直接求解,简单粗暴,没有什么想法可言,复杂度是O(N3) // 方法一,接近O(n3) int maxsofar1=0; int count=0; for (int i = 0; i < 10; ++i) { for (int j = 0; j < 10; ++j) { int sum=0; for (int k = i; k < j; ++k) { sum+=vec[k]; count++; } max

算法设计与分析(屈婉玲)pdf

下载地址:网盘下载 算法设计与分析本教材为计算机科学技术专业核心课程"算法设计与分析"教材.<算法设计与分析>以算法设计技术和分析方法为主线来组织各知识单元,主要内容包括基础知识.分治策略.动态规划.贪心法.回溯与分支限界.算法分析与问题的计算复杂度.NP完全性.近似算法.随机算法.处理难解问题的策略等.书中突出对问题本身的分析和求解方法的阐述,从问题建模.算法设计与分析.改进措施等方面给出适当的建议,同时也简要介绍了计算复杂性理论的核心内容和处理难解问题的一些新技术. &