总所周知,周一就是算法考试了,算法的理论部分是真的很难,即使能代码实现,理论也不一定能证明出来。无语子
,这份笔记都是沿着书整理的由于时间不够,很多东西是不完善的,如果你看不懂你可以私聊公众号,如果我看得懂我会回你。
①使用循环不变量证明算法的正确性
使用循环不变量证明算法的在正确性主要在于理解什么是循环不变量,循环不变量其实是在算法循环不断进行时间,循环进行前为真,循环进行后也为真的与元素有关的性质,在证明算法的正确性前,能找到这个性质是关键所在。
算法正确性的证明本质上是数学归纳法的应用,因此也分为三个步骤:
a. 奠基,证明L1正确
b. 假设Lk-1正确,证明Lk正确
c. 说明,当循环终止时,由于b步骤保证,推出终止步Ln+1正确
从而,证明算法正确。
例1:以证明插入排序算法的正确性为例:
Lj选择为:进入排序时元素a1~aj有序。
我们要说明的就是:进入排序循环时元素a1~aj有序,退出排序循环时元素a1~aj+1有序。
a. 当算法中只有一个元素时,显然有序,L1正确;
b. 假设Lk-1正确,即元素a1~元素ak-1序列已经有序,插入元素A[k],最终将该数由书上算法第七行插入到a[i-1],使得元素a1~ak有序,即Lk正确;
c. 当循环终止时,由b步骤保证,Ln正确,即a[1]~a[n]完全有序,排序完成,算法正确。
例2:以课后习题1.6为例,再进行一次证明:
Lj选择为:访问元素j前Max是当前最大值
a. 当算法只遍历到一个元素时,max为该元素,由算法第一行保证,L1正确;
b. 假设Lk-1正确,当算法遍历第k个元素时,通过条件判断,如果第k个元素大于当前最大值,则将当前最大值更新为第k个元素,否则不变,因此,遍历下一个元素时,max为当前最大值,Lk正确;
c. 当循环终止时,即循环访问最后一个元素完成,由b步骤保证,max为当前最大值,算法正确。
例3:以课后习题1.8为例,再进行一次证明:
Lj选择为:进行第j次计算前,pow是a^j-1
a. 当算法进行第一次计算前,pow应该为a的0次方,即1,在第二行初始化时完成,L1正确;
b. 假设Lk-1正确,即计算前pow为a的k-1次方当算法再进行一次计算后,通过代码第四行,pow更新为a的k次方,Lk正确;
c. 当进行第n+1次运算达到循环终点时,pow为a的n次方,即我们需要的计算结果,算法正确。
②算法的效率分析
算法的效率可以通过统计每一步的运行时间与运行次数求和得出,但我想应该不至于这样考,
以习题1.9为例:

③渐近符号
平均时间复杂度则更加复杂,在进行分析前先引入一些符号。
后面的=均是属于的含义
Θ符号:描述平均复杂度,渐近非负,被夹住(概念在书13页)
已知f(n) = 3n + 3, 证明:f(n) = Θ(n)
要证明该命题,就是要找到定义中所给的那两个正常数并与n组合将f(n)夹住,这一题很显然是很好找的,对任给n >= 3,3n <= f(n) <= 4n,即c1 = 3, c2 = 4;就可以完成证明了。
也可以按照极限方式证明,似乎按照极限方式证明,是更容易的手段,通过求解lim(n->∞) f(n)/g(n)为一个0~∞的常数,可以完成证明。
使用极限的方法可以很快的实现类似问题的证明:
f(n) = 0.5*n^2 - 3n
g(n) = n^2
证明:f(n) = Θ(g(n))
lim(n->∞) f(n)/g(n) = 0.5
f(n) = 6n^3
g(n) = n^2
证明:f(n) ≠ Θ(g(n))
lim(n->∞) f(n)/g(n) = ∞
其实,可以发现,就是f(n) 和 g(n) 最高次相同时就是平均时间复杂度了。
O符号:描述最坏情形时间复杂度,渐近上界,被压住
极限证明:与Θ非常类似,极限式存在,但不为∞
~次数g < n不行
Ω符号:描述最好情形时间复杂度,渐近下界,被踩住
极限证明:与Θ非常类似,极限式存在,但不为0
~次数g > n不行
定理1 :理解其极限含义后,定理一还是好理解的
Θ存在的充要条件是O和Ω都存在
~次数g = n
定理2: 如果f1,f2的O都存在,则f1+f2的O也存在,且为O(max{g1(n), g2(n)}(也就是取最高次数啦)
用于衡量多个算法串行时的时间复杂度
推论2.1:f(n) <= g(n) ---> f(n) + g(n) = O(g(n))
④概率分析
概率分析用于分析某算法的平均时间复杂度
核心就是假定一个算法面对的问题分布是均匀的,想办法计算其平均找查次数,例如,在n个元素中进行查找,假设查找成功的概率是均匀的,那平均查找次数应该符合以下关系:
T(n) = ∑(from k=1 to n) k* (1/n)
公式的内涵也是相当好理解的,然后运用等差等比数列的知识进行化简,就可以得到一个多项式公式,就可以写出它的Θ
好像这就是概率分析,说起来也挺简单的吼。。。
⑤分摊分析
分摊分析用于分析最坏时间复杂度~一系列运算的最坏时间复杂度
a. 合计方法
合计方法的分摊分析主要就是思路的问题,假如每一步都按照最坏时间复杂度来分析,是正确的,但上界太大,不优秀,分摊下来得不到最优结果。使用合计方法就是要想方设法发现某些最坏情况不可能达到,然后去避免他们,从而得到更小的上界。
如p25页的表格,最坏情况为到第k位前都是1,但显然最坏情况不可能一直发生,是可以获得优化的,优化方法就是表格的规律。
b. 记账方法
通过给不同的操作设置不同的费用,可以巧妙地实现复杂复杂度的计算。
但是需要注意的是,你的卡不是信用卡,不会欠款。
例1:书上的栈的例子
为入栈,出栈,多出栈设立不同费用,将入栈费用设置为2其余为0,当入栈元素都被出栈时,该上界等于实际复杂度,假如结束时有元素仍然留在栈里,上界则大于实际复杂度,因为有存款产生。
所以,n个元素入栈,时间复杂度为2n,O(n)
例2:书上计数器的例子
为0翻转为1设置2的费用,由于每一次翻转只存在一次0到1,每次循环存两块钱,假如有1到0的翻转,就用卡里的钱,我们做到第n位总共需要存2n的钱,
O(n).
c. 势能方法
势能方法和记账方法操作上是很像的,不过问题在于,我们在记账方法中的费用不易确定,势能方法通过引入一个新的函数,称为势能函数,来将每一个数据结构映射为实数,称作势能。
分摊费用 = 实际费用 + 势能变化
例1:书上的栈例子
定义势能函数为:栈中元素个数。(非负)
设push前数据结构表示为Di-1,push后表示为Di,角标表示当前为第i个运算,假设数据结构中有s个元素
所以一个push函数引起的势能差为Δ = s - (s - 1) = 1;
则其分摊费用为:
ci + Δ = 2 ci为实际费用。
同理,对multpop
Δ = -min(k,s)
分摊费用为 min(k,s) + Δ = 0;
因此最坏时间复杂度2n, O(n);
例2:书上的计算器例子
定义势能函数为第i次运算后1的个数。(非负)
由之前的分析我们可以知道,1表示没有消费的钱,因此可以用1来表示势能。
设第i步骤 复位 ti个数,翻转1个数
则 Δ = 1 - ti
分摊费用为 ti + 1 + 1 - ti = 2;
由于存在条件结构,存在分支,分摊费用应为一上界
所以实际分摊费用为 cost <= 2;
可以计算最坏情形为2n,即O(n);
本文讲解了如何使用循环不变量证明算法正确性,以插入排序、最大值求解和幂运算为例。同时涉及算法效率分析,包括平均时间复杂度(Θ)、最坏时间复杂度(O)和最好时间复杂度(Ω),并介绍了概率分析和分摊分析技巧。

被折叠的 条评论
为什么被折叠?



