刷题交流|具化运算符及数学函数的时间复杂度
1187
2021.08.29
发布于 未知归属地

前言(可不看)

因为本人在刷题中常常遇到一些常数问题:
1、明明我的算法时间复杂度是O(nlogn)的,可为什么比O(n)快?
2、明明我的算法运算次数已经达到20多亿,可是为什么能过?
3、C的sqrt()(求开方)函数不应该是O(logn)的吗?可是却像O(1)一样
在后面去问到别人时,发现都是运算符以及数学函数的底层算法没有弄清(有大数据专业的原因,课程中偏底层的课不是必修),故在此总结分享,也欢迎大家指出错误。另:如果不想深入了解底层算法,只想明白这些东西的大致时间复杂度,可以不用点开文中给出的链接

正文

运算符时间复杂度

首先必须要知道的一点是,计算机的世界里面是没有加减乘除的,甚至0,1都没有,它只知道通电了,还是没通电,因此我们实际生活常用的运算符都是由位运算得来。关于它们的实现原理有许多很好的文章,此处不再赘述。
我们以位运算与,或,非,取反,左移,右移等为时间复杂度的单位1
那么,等于,不等于,大于,小于等比较运算符(不包括C++STL中重载的运算符)复杂度也等于1(按位比较)
以32位整型为前提:
加法运算,其时间复杂度与进位次数有关系,大致是2
减法运算由于比加法多了求补码的过程,会比加法稍慢一点,复杂度大致也是2
乘法运算立马就提升了一个量级,由于采用了类似竖式计算的方法,其在加法复杂度基础上要乘以乘数的二进制位中1的个数,均摊下来,复杂度大致是10
除法运算按常理本应与乘法一个量级,但是由于其在计算机底层有个“不知道除哪个”的问题,不像乘法那么明确,故会有一位位试除,比较运算的过程,多了许多步骤,其复杂度大约是乘法的4倍以上,为40
模运算与除法是同一量级,也为40。但是编译器一般会有个优化,即比模数小的直接返回原数,因此此处40为真正进入模运算的复杂度
以上数据来自此处
需要注意的是,不同语言对运算符的具体实现方式也不同,以python为例,它不管整数的范围,统一包装为int,故其底层必然有高精度算法。令我没想到的是,它的高精度乘法似乎并不是简单的字符串模拟,就其运算时间而言,其底层似乎用了FFT算法加压位高精,一般的高精度乘法时间复杂度大约为O(n^2),这个应该小于O(nlogn),n为位数。

数学函数时间复杂度

在我以前用到sqrt()函数时,我就大致想过其时间复杂度是多少,那个时候我刚好学会二分,想着这个函数也可以用二分的算法实现,那么应该是O(logn)的时间复杂度。
但后面我发现别人写质因数分解算法时,在代码的for循环中,竟然直接使用i<=sqrt(n)来作为判断条件,而不是i*i<=n。这样不是会使时间复杂度上升很多吗?可是居然和我的差不多。我便上网搜了下其底层算法,了解到了牛顿迭代法。这个方法大致思想是用切线去不断逼近曲线的根,好像时间复杂度很小,甚至可以当作O(1),其在C其他的许多数学函数中也有应用。

基于现代计算机的时间复杂度计算方式

因为现在电脑的速度很快,按照许多年前的算法书上教的计算机一秒运行一千万次的标准算肯定不对。我观察的参数是CPU多少hz(1s运算多少次),这个参数确实不一定精确,毕竟要考虑硬件的物理条件,但大致没问题。现在笔记本如果是4000RMB(按我同学的话说,就是中上品质)以上的话,20GHz(1秒20亿次)应该有。这个运算次数根据我的测试应该是位运算的次数。那么以后大家就可以用我上面提到的参数去进行更精准的时间复杂度估计,并在以后的程序编写中有意识的减少除法与模运算的使用。(附:力扣的时限经本人二分测量大致是1s

一些真实事例(可不看)

1、我曾经用暴力位运算枚举,以实打实二十多亿次运算的算法在牛客网上过了一道题(时限1s)
2、我曾经写过和别人一样思路的O(nlogn)算法,但是我的超时了,他的过了,因为我用了太多除法,他以乘法为主
3、我在写最近ACM区域赛的题目时,发现出题人如果希望我们写O(n)算法,然后卡掉O(nlogn)的算法(时限1s),这个n通常给到了一千万,而不像很多年前的二十万或者十万。

评论 (0)