“今天我们来讲一讲这个gcd……”
“滚滚滚,谁用你讲”
今天我要说的确实是和gcd有关,不过是一个稍深一点的问题:时间复杂度。
多数人都知道,gcd很快。但快到什么程度呢?有什么方法去刻画gcd的时间复杂度呢?
在《离散数学》上给出了一个证明(P77),可以证明,gcd的运行次数不会超过2*log2(n+1)n为较小数。
这样虽然很快了,但是他还不能太精准的刻画出gcd的运行次数。
在上述证明的旁边,还给出了一个结论:拉梅定理:gcd的运行次数,不会超过较小数十进制位数的5倍,也就是5*log10(n)
如何证明他的正确性呢?
在经过一系列的思(bai)索(du)之后,一个神奇的证明过程出现了:
先回到gcd的运算过程:
取a,b(a<b)
令 r(0) = a; r(1) =b;
则有以下等式成立
r(0) = q(1)*r(1) +r(2);
r(1) = q(2)*r(2) +r(3);
r(2) = q(3)*r(3) +r(4);
……
r(n-2) = q(n-1)*r(n-1) +r(n);
r(n-1) = q(n)*r(n) +0;
由余数和除数的关系可知,r(1)>r(2)>r(3)>r(4)……r(n-1)>r(n)>0
即r(i)是递减的。
又因为r(i)>r(i+1)
所以 r(i)/r(i+1)>=1;
即q(i)>=1
又因为最后一个式子 r(n-1) = q(n)*r(n); 要想满足r(n-1)>r(n) q(n)必须大于等于2;
所以上面的式子可以改写成:
r(0) >= r(1) +r(2);
r(1) >= r(2) +r(3);
r(2) >= r(3) +r(4);
……
r(n-2) >= r(n-1) +r(n);
r(n-1) > r(n);
可以发现,r之间的关系其实是类似于斐波那契数样式的关系。
斐波那契数列的引入:
F(1) = 1; F(2) = 1; F(3) = 2; F(4) = 3……
我们可以看出,r(n) >= 1 = F(2) ; r(n-1) > r(n); r(n-1)>=2 = F(3)
那我们可不可以推出: r(n-j) >= F(j+2) 呢 ?
能!
因为: r(n-j) = q(n-j+1) * r(n-j+1) + r(n-j+2)
>= r(n-j+1) + r(n-j+2)
>= F(j+1) + F(j)
= F(j+2)
也就得出了这样一个结论:r(n-j) >= F(j+2) 当 j = n-1 时, r(1) >= F(n+1)
同时,F(n+1)>=φ^(n-1) φ = (sqrt(5)+1)/2 (这个的证明很简单,大家可以自己试一试)
则可以知道r(1)>=φ^(n-1)
变成对数形式就是:
logφ[r(1)] >= n-1
φ是一个无理数,不好估计,可不可以将他换成一个整数呢?
考虑log10(φ) = 0.20898764024997873376927208923756 > 0.2
则有 log10(φ) * logφ[r(1)] = log10[r(1)]> (n-1) *0.2
整理一下:5*log10[r(1)] > n-1
把1干掉: 5*log10[r(1)] >= n
一个比较简洁的公式就出现了
现在,可以说,gcd运行的次数一定不超过较小数位数的五倍。
当然这些都是扯淡,算复杂度的时候把它当成log就完了。
说点干的:一行gcd:
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
当然这种逼格不够,换一种方式才更好:
ll gcd(ll a,ll b) {while(b^=a^=b^=a%=b);return a;}