当前位置: 首页 > 文档资料 > larva 中文文档 >

伪多项式复杂度

优质
小牛编辑
139浏览
2023-12-01

上篇的标记算法中,谈到这个O(K)的算法是一个指数级复杂度的算法,其实对那道题目本身来说,K是固定的,既然不是输入,那也无所谓复杂度,之所以有O(K)这种说法,是把K当做一种输入了,这是看待输入的角度问题,倒不用深究。考虑一个更简化的算法(python代码,设输入n是正整数):

def f(n):   
    i = 0   
    while i < n:   
        i += 1   

这个算法在任意整数输入下都返回None,我们关注的重点是它的时间复杂度,根据python的相关机制,赋值操作是一个指针修改,可以认为是常数时间完成,于是这个算法执行了n次比较运算和加一运算

之所以用python举例,因为python的整数和C的整数类型不同,是不限制位数的(由于内存有限,实际还是有限制的,但也足够大了),也就是说,不能将整数的所有运算都看做是常数时间的,因为数字可能非常大,在内部是用一个数组来存储。最简单的例子,两个数字a和b相加,需要做的工作和它俩的长度有关,因此加法时间复杂度是和lga,lgb线性相关

不过在这个例子中,我们可以证明比较和加一运算都是平均常数时间的,如果两个大数比较,先看长度,长的那个显然大,如果一样长,则从前往后用类似strcmp的算法,根据前面某篇的讨论,strcmp的平均时间复杂度是O(1);另一方面,虽然上面说加法跟数字的位数线性相关,但若是加一运算,只有在产生连续进位的时候运算时间才和长度有关,而连续加一操作平均每次是常数时间(参考算法导论的平摊分析一章)

于是我们就可以说,上面这个算法的复杂度是Θ(n),这个结论没有错,但这个算法是一个指数级复杂度,原因在于,输入规模并不是和n线性相关的,对于一个算法的输入规模,有一个明确简单的定义,就是输入的长度

由此,可以把输入分为两种,数据输入和数值输入,假设使用2进制作为表示形式,则对于一个数值N来说,它的输入规模是lgN,当然,如果使用base大于2的进制,输入长度会短,但从渐进意义上来说是一样的,因为当x和y都大于1的时候,Θ(log(N,x))=Θ(log(N,y)),因此用二进制就能说明问题了。时间复杂度为Θ(T(N)),可以认为当输入规模变为原来的k倍时,算法运行时间大约会增长到原来的T(k)倍这个级别,因此就上面这个算法来说,如果输入规模增长到原来的k倍,那输入的数值n会变成原来的k次方,因此,由于n是数值输入,Θ(n)改写成以输入规模N的表示,就是Θ(2^N),是一个指数级别复杂度

对于数据输入,就比较好理解了,如果不考虑每个数据本身的数值长度(有时候还是要考虑),则数据输入的规模就是其数量

假如一个算法有数值输入,且其时间复杂度可以表示为输入数值(不是输入规模)的多项式,则根据上面的讨论,实际应该是一个指数时间的算法,但这种情况毕竟和数据输入规模的指数级复杂度有些不同,一般来说,直观上也习惯用输入数值来表示复杂度,对于这种看上去像是多项式(数值的多项式)而实际代表算法是指数级(输入规模作为指数)的复杂度,称其为伪多项式复杂度

很多算法具有伪多项式复杂度,例如,采用从2开始试除的方式判定一个整数N是否是素数,需要做N^(1/2)次除法,而每次除法的时间跟lgN有关,乍一看这个多项式也不高,但由于N会随着输入规模很快增长,仍然是一个指数时间的算法,用它判定素数是很低效的 P.S.听说已经有了多项式时间判定素数的算法,时间复杂度是O((lgN)^6)到O((lgN)^12),当然这里由于N会随着输入规模的线性增长而指数级增长,所以不妨设输入规模M=lgN,就容易理解了

利用动态规划,0-1背包问题可以有一个O(NW)的算法,由于W是数值输入,因此这个算法是伪多项式时间的。事实上,通过计算理论中的方法,可以证明0-1背包问题是一个NPC问题,如果不理解伪多项式,就很难理解为何它有一个O(NW)的算法,却是NPC的

存在伪多项式时间复杂度算法的NPC问题,一般称为弱NPC问题,反之则是强NPC问题,弱NPC问题似乎更“接近”P,在相关问题的研究中经常引起人们更大的兴趣