算法设计策略有哪些
算法设计策略如下:
1、分治html
分治法的设计思想是,将一个难以直接解决的大问题,分割成k个规模较小的子问题,这些子问题相互独立,且与原问题相同,而后各个击破,分而治之。算法。
2、动态规划spa
动态规划法与分治法相似,其基本思想也是将原问题分解成若干个子问题。这种状况下若用分治法会对一些子问题进行屡次求解,这显然是没必要要的。动态规划法在求解过程当中把全部已解决的子问题的答案保存起来,从而避免对子问题重复求解。
3、贪心
当一个问题具备最优子结构性质时,可用动态规划法求解。但有时会有比动态规划更简单更直接效率更高的算法——贪心法。贪心法老是作出在当前看来最好的选择,也就是说贪心法并不从总体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。
4、回溯
回溯法是对问题的解空间树进行深度优先搜索 ,可是在对每一个节点进行DFS以前,要先判断该节点是否有可能包含问题的解。若是确定不包含,则跳过对以该节点为根的子树的搜索,逐层向其祖先节点回溯。若是有可能包含,则进入该子树,进行DFS。
5、分支限界
回溯法是对解空间进行深度优先搜索,事实上任何搜索遍整个解空间的算法都可解决问题。因此采用通用图搜索的任何实现做为搜索策略都可解决问题,只要作到穷举便可。除了深度优先搜索以外,咱们还可采用广度优先搜索,而分支限界法则是对解空间进行优先级优先搜索。
理论上,许多问题可以用穷举搜索的办法来求解。这种解题策略会直截了当地试遍所有的可能解,直接找到问题的解为止。采用穷举搜索时,很少需要独具匠心的设计,因此,如果一个问题确定要用这种策略来求解的话,就很少需要人工计算,而基本上是为计算机准备的。穷举搜索的最大局限性在于它的效率低下,通常,如果可能解的数量随着问题规模而呈指数增长或更快的话,那么这条途径不仅对人类来说遥不可及,计算机也只能望而兴叹了。
策略模式(Strategy Pattern),定义了一系列的算法,将每一种算法封装起来并可以相互替换使用,策略模式让算法独立于使用它的客户应用而独立变化。
策略模式是处理算法的不同变体的一种行为模式,通过在抽象策略中定义算法接口或封装算法标识,实现该抽象策略的具体子类成为一个单独的算法,即具体策略。策略模式使用多个类来区别不同的行为,使用策略模式避免暴露复杂的、与算法相关的内部数据结构。当一个类中的操作以多个条件分支语句的形式出现的时候,可以使用策略模式将相关的条件分支移入各自的具体策略类中以代替这些条件语句,从而减少系统处理的复杂度。
1、递归算法改为循环。这大概可以较少30%左右的运行时间。但是,递归的代码相对更简洁,可读性更好。
2、将重复发生的计算提取到循环结构之外,或者建立一个数组保存起来,把重复计算改为查询数组。许多情况下,这可以节省一半甚至90%以上的时间。
3、如果可能,将浮点运算改为整数运算。减少的运行时间与上述第2种做法相当。
4、在递归过程中插入条件检查,提前剪枝退出。对于有些枚举量超大的计算,可以提高几十倍甚至上万倍的速度,节省大量的时间。
穷举法的运用关键在于解决两个问题:
在运用穷举法时,容易出现的问题是可能解过多,导致算法效率很低,这就需要对列举可能解的方法进行优化。
以题1041--纯素数问题为例,从1000到9999都可以看作是可能解,可以通过对所有这些可能解逐一进行判别,找出其中的纯素数,但只要稍作分析,就会发现其实可以大幅度地降低可能解的范围。根据题意易知,个位只可能是3、5、7,再根据题意可知,可以在3、5、7的基础上,先找出所有的二位纯素数,再在二位纯素数基础上找出三位纯素数,最后在三位纯素数的基础上找出所有的四位纯素数。
2、分治法分治法也是应用非常广泛的一种算法设计策略,其思想是将问题分解为若干子问题,从而可以递归地求解各子问题,再综合出问题的解。
分治法的运用关键在于解决三个问题:
我们熟知的如汉诺塔问题、折半查找算法、快速排序算法等都是分治法运用的典型案例。
以题1045--Square
Coins为例,先对题意进行分析,可设一个函数f(m,
n)等于用面值不超过n2的货币构成总值为m的方案数,则容易推导出:
f(m,
n)
=
f(m-0*n*n,
n-1)+f(m-1*n*n,
n-1)+f(m-2*n*n,
n-1)+...+f(m-k*n*n,
n-1)
这里的k是币值为n2的货币最多可以用多少枚,即k=m/(n*n)。
也很容易分析出,f(m,
1)
=
f(1,
n)
=
1
对于这样的题目,一旦分析出了递推公式,程序就非常好写了。所以在动手开始写程序之前,分析工作做得越彻底,逻辑描述越准确、简洁,写起程序来就会越容易。
3、动态规划法
动态规划法多用来计算最优问题,动态规划法与分治法的基本思想是一致的,但处理的手法不同。动态规划法在运用时,要先对问题的分治规律进行分析,找出终结子问题,以及子问题向父问题归纳的规则,而算法则直接从终结子问题开始求解,逐层向上归纳,直到归纳出原问题的解。
动态规划法多用于在分治过程中,子问题可能重复出现的情况,在这种情况下,如果按照常规的分治法,自上向下分治求解,则重复出现的子问题就会被重复地求解,从而增大了冗余计算量,降低了求解效率。而采用动态规划法,自底向上求解,每个子问题只计算一次,就可以避免这种重复的求解了。
动态规划法还有另外一种实现形式,即备忘录法。备忘录的基本思想是设立一个称为备忘录的容器,记录已经求得解的子问题及其解。仍然采用与分治法相同的自上向下分治求解的策略,只是对每一个分解出的子问题,先在备忘录中查找该子问题,如果备忘录中已经存在该子问题,则不须再求解,可以从备忘录中直接得到解,否则,对子问题递归求解,且每求得一个子问题的解,都将子问题及解存入备忘录中。
例如,在题1045--Square
Coins中,可以采用分治法求解,也可以采用动态规划法求解,即从f(m,
1)和f(1,
n)出发,逐层向上计算,直到求得f(m,
n)。
在竞赛中,动态规划和备忘录的思想还可以有另一种用法。有些题目中的可能问题数是有限的,而在一次运行中可能需要计算多个测试用例,可以采用备忘录的方法,预先将所有的问题的解记录下来,然后输入一个测试用例,就查备忘录,直接找到答案输出。这在各问题之间存在父子关系的情况下,会更有效。例如,在题1045--Square
Coins中,题目中已经指出了最大的目标币值不超过300,也就是说问题数只有300个,而且各问题的计算中存在重叠的子问题,可以采用动态规划法,将所有问题的解先全部计算出来,再依次输入测试用例数据,并直接输出答案。
4、回溯法回溯法是基于问题状态树搜索的求解法,其可适用范围很广。从某种角度上说,可以把回溯法看作是优化了的穷举法。回溯法的基本思想是逐步构造问题的可能解,一边构造,一边用约束条件进行判别,一旦发现已经不可能构造出满足条件的解了,则退回上一步构造过程,重新进行构造。这个退回的过程,就称之为回溯。
回溯法在运用时,要解决的关键问题在于:
回溯法的经典案例也很多,例如全排列问题、N后问题等。
5、贪心法贪心法也是求解最优问题的常用算法策略,利用贪心法策略所设计的算法,通常效率较高,算法简单。贪心法的基本思想是对问题做出目前看来最好的选择,即贪心选择,并使问题转化为规模更小的子问题。如此迭代,直到子问题可以直接求解。
基于贪心法的经典算法例如:哈夫曼算法、最小生成树算法、最短路径算法等。
设计算法的原则:
1、正确性:算法的正确性是指算法至少应该具有输入、输出和加工处理无歧义性、能正确反映问题的需要、能够得到问题的正确答案。
2、可读性:设计算法的目的,一方面是为了让计算机执行,但还有一个重要的目的就是为了便于他人的阅读,让人理解和交流,自己将来也可阅读。如果可读性不好,时间长了自己都不知道写了什么,可读性是评判算法(也包括实现它的程序代码)好坏很重要的标志。
3、健壮性:当输入的数据非法时,算法应当恰当地做出反应或进行相应处理,而不是莫名其妙的输出结果。并且处理出错的方法不应是中断程序的执行,而应是返回一个表示错误或错误性质的值,以便于在更高的抽象层次上进行处理。
4、高效率与低存储量:通常,算法的效率指的是算法的执行时间;算法的存储量指的是算法执行过程中所需要的最大存储空间,两者的复杂度都与问题的规模有关。算法分析的任务是对设计的每一个具体的算法,利用数学工具,讨论其复杂度,探讨具体算法对问题的适应性。
扩展资料:
算法的“正确”通常在用法上有很大的差别,大体分为以下4个层次:
1、算法程序没有语法错误;
2、算法程序能够根据正确的输入的值得到满足要求的输出结果;
3、算法程序能够根据错误的输出的值满足规格说明的输出结果;
4、算法程序对于精心设计、极其刁难的测试数据都能满足要求的输出结果。
对于这4层含义,层次要求最低,因为仅仅没有语法错误实在谈不上是好的算法。而层次(4)是最困难的,人们几乎不可能逐一验证所有的输入都得到正确的结果。因此,算法的正确性在大部分情况下都不可能用程序来证明,而是用数学方法证明的。
在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)……
任何一个可以用计算机求解的问题所需的计算时间都与其规模有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于n个元素的排序问题,当n=1时,不需任何计算。n=2时,只要作一次比较即可排好序。n=3时只要作3次比较即可,…。而当n较大时,问题就不那么容易处理了。要想直接解决一个规模较大的问题,有时是相当困难的。
二、基本思想及策略
分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。
如果原问题可分割成k个子问题,1<k≤n,且这些子问题都可解并可利用这些子问题的解求出原问题的解,那么这种分治法就是可行的。由分治法产生的子问题往往是原问题的较小模式,这就为使用递归技术提供了方便。在这种情况下,反复应用分治手段,可以使子问题与原问题类型一致而其规模却不断缩小,最终使子问题缩小到很容易直接求出其解。这自然导致递归过程的产生。分治与递归像一对孪生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。
三、分治法适用的情况
分治法所能解决的问题一般具有以下几个特征:
1) 该问题的规模缩小到一定的程度就可以容易地解决
2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
3) 利用该问题分解出的子问题的解可以合并为该问题的解;
4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;
第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;、
第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。
第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
四、分治法的基本步骤
分治法在每一层递归上都有三个步骤:
step1 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
step2 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
step3 合并:将各个子问题的解合并为原问题的解。
它的一般的算法设计模式如下:
Divide-and-Conquer(P)
1. if |P|≤n0
2. then return(ADHOC(P))
3. 将P分解为较小的子问题 P1 ,P2 ,…,Pk
4. for i←1 to k
5. do yi ← Divide-and-Conquer(Pi) △ 递归解决Pi
6. T ← MERGE(y1,y2,…,yk) △ 合并子问题
7. return(T)
其中|P|表示问题P的规模;n0为一阈值,表示当问题P的规模不超过n0时,问题已容易直接解出,不必再继续分解。ADHOC(P)是该分治法中的基本子算法,用于直接解小规模的问题P。因此,当P的规模不超过n0时直接用算法ADHOC(P)求解。算法MERGE(y1,y2,…,yk)是该分治法中的合并子算法,用于将P的子问题P1 ,P2 ,…,Pk的相应的解y1,y2,…,yk合并为P的解。
五、分治法的复杂性分析
一个分治法将规模为n的问题分成k个规模为n/m的子问题去解。设分解阀值n0=1,且adhoc解规模为1的问题耗费1个单位时间。再设将原问题分解为k个子问题以及用merge将k个子问题的解合并为原问题的解需用f(n)个单位时间。用T(n)表示该分治法解规模为|P|=n的问题所需的计算时间,则有:
T(n)= k T(n/m)+f(n)
通过迭代法求得方程的解:
递归方程及其解只给出n等于m的方幂时T(n)的值,但是如果认为T(n)足够平滑,那么由n等于m的方幂时T(n)的值可以估计T(n)的增长速度。通常假定T(n)是单调上升的,从而当mi≤n<mi+1时,T(mi)≤T(n)<T(mi+1)。
六、可使用分治法求解的一些经典问题
(1)二分搜索
(2)大整数乘法
(3)Strassen矩阵乘法
(4)棋盘覆盖
(5)合并排序
(6)快速排序
(7)线性时间选择
(8)最接近点对问题
(9)循环赛日程表
(10)汉诺塔
七、依据分治法设计程序时的思维过程
实际上就是类似于数学归纳法,找到解决本问题的求解方程公式,然后根据方程公式设计递归程序。
1、一定是先找到最小问题规模时的求解方法
2、然后考虑随着问题规模增大时的求解方法
3、找到求解的递归函数式后(各种规模或因子),设计递归程序即可。
1.
穷举法
2.
贪心法
3.
分治法
4.
回溯法
5.
分枝限界法
6.
动态规划法