算法及算法的评价

算法及算法的评价

算法

算法是对特定问题求解步骤的一种描述,它是指令的有点序列,其中每一条指令表示一个或多个操作。


算法的五个重要特性

有穷性、确定性、可行性、具有输入、具有输出。


好算法的特征

通常一个好的算法应该达到以下目标:

1.正确性:算法应当能正确地解决求解问题。

2.可读性:算法应当具有良好的可读性,以助人们理解。

3.健壮性:当输入非法的数据时,算法也能适当的做出反应或进行处理,而不会产生莫名奇妙的输出结果。

4.效率与低存储量需求:效率是指算法的执行时间、存储量需求是指算法执行过程中所需要的最大存储空间,这两者都与问题规模有关。


渐进分析:复杂度和大O记号的引入

回归最初的问题,随着问题的增长,计算成本应该如何增长?这里的我们只关心足够大的问题规模,注重考察成本的增长趋势。

渐进分析:在问题规模足够大后,计算成本增长的分析。

时间复杂度和空间复杂度

是算法的效率的度量。

时间复杂度:一个语句的频度是指该语句在算法中被重复执行的次数。算法中的所有语句的频度之和记作\(T(n)\),它是该算法问题规模n的函数,时间复杂度主要分析\(T(n)\)的数量级,又因为算法中基本运算(最深层循环内的语句)的频度与\(T(n)\)同数量级,所以通常采用算法中基本运算的频度\(f(n)\)来分析算法的时间复杂度。因此,算法的时间复杂度记为:\(T(n) = O(f(n))\)

大O记号(big-O notation)

\(T(n) = O(f(n)) \quad\) \(iff \quad\) \(∃ \ c > 0\),当 \(n >> 2\)后,有\(T(n) < c*f(n)\)

\(\sqrt{5n*[3n*(n+2) + 4] + 6} < \sqrt{5n * [6n^2 + 4] + 6} < \sqrt{35n^3 + 6} < 6 * n^{1.5} = O(n^{1.5})\)

\(T(n)\)相比,\(f(n)\)更为简洁,但依然反映着前者的增长趋势。

常系数可忽略:\(O(f(n)) \ = \ O(c*f(n))\)

低次项可忽略:$O(n^a + n^b) = O(n^a) \(, 当\)a > b > 0$

时间复杂度:\(O(1)\)

常数(constant function)

\(2 = 2013 = 2013 * 2013 = O(1)\),甚至\(2013^{2013} = O(1)\) //含RAM各基本操作

这类算法效率最高(总不能奢望不劳而获吧)

什么样的代码段对应于常数执行时间? (应具体分析)

一定不含循环?

for(i = 0; i < n; i += n / 2013 + 1);
for(i = 1; i < n; i = 1 << i)  // log*n,几乎为常数

一定不含分支转向?

if((n + m) * (n + m) < 4 * n * m) goto UNREACHABLE; //不考虑溢出

一定不能有(递归调用)?

if(2 == (n * n) % 5) O1(n);

时间复杂度:\(O(log^cn)\)

**对数\(log(n)\) **

\(lnn\)| $ lgn $| $log_{100}n $| \(log_{2013} n\)

**常底数无所谓 **

\({\forall}\ a,\ b > 0, \ log_an = \ log_ab * \ log_bn = \ Θ(log_bn)\)

**常数次幂无所谓 **

\({\forall}\ c \ > \ 0 ,\ logn^c \ = \ c * logn = Θ(logn)\)

**对数多项式(ploy-log function) **

\(123*log^{321}n \ + \ log^105(n^2 - n + 1) \ =\ Θ(log^{321}n)\)

这类算法非常有效,复杂度无限接近于常数:\({\forall} \ c \ > \ 0, \ logn \ = \ O(n^c)\)


时间复杂度:\(O(n^c)\)

**多项式(ploynomial function) : **

\(100n + 200 = O(n)\)

\((100n - 500)(20n^2 - 300n = 2013) \ = \ O(n * n^2) \ = \ O(n^3)\)

\((2013n^2 -20) / \ (1999n - 1) \ = \ O(n^2/n) \ = O(n)\)

一般地:\(a_kn^k + a_{k-1}n^{k-1} + ... + a_1n + a_0 \ = \ O(n^k), \ a_k > 0\)

线性(linear function):所有\(\ O(n)\)类函数

\(O(n)\)\(O(n^2)\):编程算法题的主要覆盖范围

幂:\([(n^{2013} -24n^{2009})^{1/3} + 512n^{567} - 1978n^{123}]^{1/11} \ = \ O(n^{61})\)


时间复杂度:\(O(2^n)\)

**指数(exponential function): **\(T(n) \ = \ a^n\)

\({\forall} \ c \ > \ 1,\ n^c \ = \ O(2^n)\)

\(n^{1000} \ = \ O(1.0000001^n) \ = \ O(2^n)\)

\(1.0000001^n = Ω(n^{1000})\)

这类算法的计算成本增长极快,通常被认为不可忍受。

\(O(n^c)\)\(O(2^n)\)是从有效算法到无效算法的分水岭,很多问题\(O(2^n)\)的算法往往显而易见,然后设计出\(O(n^c)\)的算法却极其的不易,甚至,有时候注定是徒劳无功的,我们把这些问题分为NP和非NP问题。


复杂度层次


最坏时间复杂度:指在最坏的情况下,算法的时间复杂度。

平均时间复杂度:指所有输入等可能的情况下,算法的期望运行时间。

最好时间复杂度:值在最好的情况下,算法的时间复杂度。

均摊时间复杂度:在代码执行的所有复杂度情况中绝大部分是低级别的复杂度,个别情况是高级别复杂度且发生具有时序关系时,可以将个别高级别复杂度均摊到低级别复杂度上。基本上均摊结果就等于低级别复杂度。

举例:有一个长度为n的数组,如果数组没满,就往里插入一个数,如果数组满了,就遍历求和.那么绝大多数情况下都是\(O(1)\),只有最后一次是\(O(n)\),均摊以后就是\(O(1)\)

复杂度振荡:数组等数据结构的扩容和缩容之间出现,原因是没有处理好扩容和缩容因子之间的关系。


平均分析 VS 分摊分析

平均复杂度或期望复杂度(average/expected complexity)

根据数据结构各种操作出现概率的分布,将对应的成本加权平均。

各种可能的操作,作为独立事件分别考察,割裂了操作之间的相关性连贯性,往往不能准确地评判数据结构和算法的真实性能。

分摊复杂度(amortized complexity)

对数据结构连续地实施足够多次的操作,所需总体成本分摊至单次操作。

从实际可行的角度,对一系列操作做整体的考量,更加忠实地刻画了可能出现的操作序列,可以更为精准地评判数据结构和算法的真实性能。


空间复杂度

算法的空间复杂度\(S(n)\),定义为该算法所耗费的辅助存储空间,它是问题规模n的函数。渐进空间复杂度通常简称为空间复杂度,记作\(S(n) = O(g(n))\)

算法的 原地工作是指算法所需的辅助存储空间是常量,即O(1)。

posted @ 2018-12-20 13:40  schrodingercatss  阅读(2793)  评论(0编辑  收藏  举报