时间复杂度分析
级数求和
这个有高数知识很容易得到。
第二个收敛级数很容易证明收敛。第三个收敛级数没怎么见过,看不透它的规律。
分数主要在有随机性的场合有用,随机性的复杂度求的是期望,上例是抛硬币,求第一次出现正面时的复杂度,我们只能借助概率的期望来求。如果哟多个随机参与,那么就会出现分数相加。
调和级数和ln差一个欧拉常数。
对数级数的结论可用斯特林公式得到。
可以用面积计算复杂度。
虽然j+=2013,但是不改变复杂度。
i=2的时候才能进内循环,此时循环一次,而j+=j其实和左移的作用一样。
一般算法
这个例子说明有些问题的复杂度可以和问题规模无关。
第一次内循环是全部冒泡排序,这时候最大的肯定在最后了(浮到最后面了),这时候最大的不需要动了。所以要n--,接下来次大的浮上来。每轮冒泡前都认为是有序的,如果发现无序,在内循环清除有序标志。如何退出循环呢?如果sorted在内循环结束仍然等于True就会结束,因为!true=false。
冒泡排序升序是把最大的放到最后,然后次大的......,降序反之。
那么显然经过有限步就可以排好,最多n趟。
由不变性和单调性得到正确性是算法分析的重要思路。
封底估算
实际上就是抓住矛盾的主要方面。在复杂度计算事常用。
迭代和递归
迭代乃人工,递归方神通
To iterate is human to recurse. divine
凡治众如治寡,分数是也
The control of a large force is
the same principle as
the control of a few men
it is merely a question of
dividing up their numbers
递归的代码会比较简洁,但是可能导致栈溢出。所以迭代的效率会更高。
空间复杂度为O(1)。其实迭代也用到了减治的思想,问题规模为n,没迭代一次,问题规模就减少1。
平凡的问题事很容易求解的。
递归分析
但这种递归跟踪的方法对于复杂一点的递归就不行了。它是一种几何的画图的方法。
下面是更通用的代数方程的方法:
这个就是求数列的通项,已知数列初项。
显然是O(n)。
递推式:T(n)=T(n-2)+O(2)。T(1)=0,T(2)=1。
T(n)-n=T(n-2)-(n-2)
根据简单的递推式求数列通项是高中的基础知识了,或者说是解差分方程。
这个思路很好理解,但是效率很差。
这个是每次同时维护x1和x2,但是在最坏的情况并没有改进。
这样的比较次数最坏只需要两次,因为最大值必定在两个小区间内的最大值诞生,而次大值在获胜区间的次大和落败区间的最大间产生。
T(n)+2=2[T(n/2)+2]=.........=(n/2)T(2)+n
T(2)=1
T(n)+2=n/2+n-2
这个是偶数的时候
奇数的时候是T(3)=2。总之比O(2n)好,不过用渐进复杂度的角度来说是一样的。
分治是可以降低复杂度的。
动态规划
动态规划可以说是先用递归找出问题的本质后有一个初步的解然后再用迭代的形式。
这个解差分方程的过程大家应该在数列,线性代数里都见过了。
第43项需要一秒。第67项需要1天。
普通递归的效率太差了,是个NP的。
递归追踪分析:
看到递归效率低下的原因在于重复计算。那么如何避免重复计算。
这个就是O(n)的复杂度,而且空间复杂度为O(1)。
最长公共子序列(LCS)
先看递归算法:
、
对角线是对应字符相等的情况,而不等的话分出两条路,一条向右,一条向下,遇到相同的就走对角线。那么整个问题就是从左上角走到右下角的所有路径的最大值(不是距离的最大值)。
其实这里面也有大量的计算雷同,假设n,m是原始问题。那么中间的a,b点的经过次数就会很多。
最坏情况是水平竖直走的,那么这就是一个走楼梯问题,从n,m到a,b只能向左或者向上走,那么从n,m到a,b的走法为:
显然必须走m+n-a-步,那么横着走n-a步,那么在哪些地方横着走呢?就用到了组合数。
迭代方法:
空的都是0,那么就从左上角开始迭代,先比较两个字符串首字符是否相同,不同的话置0,相同的置1,然后先填满行,这行只要有1出现右边就全为1了,列一样的规则。然后是第二行,遇到相同的则取左上角的加1,如果不等,取左上的最大值。如此建立整张表格。