最近杨振宁和姚期智的回到中国国籍的事情非常热闹,作为一个在中国想做coding相关工作的人,你就不应该不知道清华姚班这个神奇的存在。老子这辈子是没有一丝机会达到那个高度了,但是我要努力使我儿子有机会够到那个高度,哈哈哈,开玩笑。但是,这和这个章节的题目有什么关系呢?那我因为我早上在知乎上看了一下Aady Yao的研究成果:
早在博士就读期间,姚期智提出了随机化算法复杂度的论证,而如今已经成为研究者无人不知的重要工具。
在 1977 年的论文中,姚期智提出了Yao's min-max principle,这一原理成为了推理随即算法与复杂度的基本技术,也已经应用于属性测试与学习理论等领域。
没错,那就是关于复杂度的分析,真的是太难了,要对复杂度有一个深刻的理解,我觉得首先要深刻理解计算机系统。即便如此,我也还是得写一点简单的总结, 万事都是要开头,你才会继续去做的!
常数O(1)
这里有一个简单的问题,我们要对三个数进行排序,并输出最大元素。不妨取前三个元素x=S[0], y=S[1], z=S[2], 这一步只需要执行三次(从特定单元提取元素),耗费O(3)时间。接下来,为确定这三个元素的的大小顺序,最多需要三次排序,也是O(3)时间,最后输出最大的元素需要O(1)时间。
T(n) =O(3)+O(3)+O(1)=O(7)=O(1)
运算时间可表示和度量为T(n)=O(1)的这一类算法,统称为”常数时间复杂度算法”,也称为就地算法。
对数O(
logn
)
考察如下问题:对于任意非负整数,统计其二进制展开中位数1的总数。
int countOnes(unsigned int n){
int ones=0;
while(n>0){
ones+=(1&n);
n>>1;
}
}
根据右移运算的性质,没右移一位,n至少缩减一半,也就是说,至多经过1+
log2n
次循环,n必然缩减至0,从而算法终止。实际上从另一个角度上讲,1+
log2n
恰好为n二进制展开的总位数,每次循环都将其右移一位,总的循环数也应是1+
log2n
。
所以countOnes的算法执行时间主要由循环的次数决定:
O(1+
log2n
)=O(
log2n
)
在对数时间复杂度里面,
log2n
实际上与底数无关,底数2可以换成其他任何数。
线性O(n)
关于线性时间复杂度,我们可以改进一下countOne算法:
int countOnes(unsigned int n){
int ones=0;
while(n>0){
count++
n&=n-1;
}
}
这个算法巧妙地通过位运算技巧,自低向高逐个地将位数1转置为0。
以上计算过程中,仅涉及整数的减法和位与运算各一次。若不考虑机器的位长限制,各只需要O(1)时间因此,这个新算法的运行时间,应线性正比于n的二进制展开中位数1的实际数目。
指数O( 2n )
--int64 power2BF_I(int n){
__int64 pow=1;
while(n-- >0){
pow<<=1;
return pow;
}
这里的power2BF_I算法是由n轮迭代组成的,各需做一次累乘和一次递减,均属于基本操作,故整个算法时间共需O(n)时间。但是若以输入指数n的二进制位数r=1+ log2n 作为输入规模,则运行时间为O( 2n )。