关于大o:欧几里得算法的时间复杂度

Time complexity of Euclid's Algorithm

我很难确定欧几里得最大公分母算法的时间复杂性是什么。该伪码算法为:

1
2
3
4
5
6
function gcd(a, b)
    while b ≠ 0
       t := b
       b := a mod b
       a := t
    return a

这似乎取决于a和b。我的想法是时间复杂性是o(a%b)。对吗?有没有更好的方法来写呢?


分析欧几里得算法的时间复杂性的一个技巧是遵循两次迭代的结果:

1
a', b' := a % b, b % (a % b)

现在A和B都会减少,而不是只减少一个,这使得分析更容易。您可以将其分为以下几种情况:

  • 小A:2a <= b
  • 小B:2b <= a
  • 小A:2a > b,但a < b
  • 小B:2b > ab < a
  • 相等:a == b

现在我们将显示,每一个案例至少减少了四分之一的EDOCX1×11的总数:

  • 微小A:b % (a % b) < a和EDCX1〔4〕,因此b至少减少一半,因此EDCX1〔11〕至少由25%减少。
  • 微小B:a % b < b和EDCX1〔5〕,因此a至少减少一半,因此EDCX1〔11〕至少由25%减少。
  • 小A:b将成为b-a,这比EDCX1〔24〕小,EDOCX1×11〕至少通过EDCX1〔16〕减小。
  • 小B:a变为a-b,小于a/2,至少减少a+b
  • 相等:a+b下降到0,明显减少a+b,至少减少25%

因此,通过案例分析,每两步至少使a+b减少25%。在a+b被迫降到1以下之前,这种情况可能发生的次数最多。到达0之前的步骤总数(S)必须满足(4/3)^S <= A+B。现在就开始吧:

1
2
3
4
5
6
7
8
(4/3)^S <= A+B
S <= lg[4/3](A+B)
S is O(lg[4/3](A+B))
S is O(lg(A+B))
S is O(lg(A*B)) //because A*B asymptotically greater than A+B
S is O(lg(A)+lg(B))
//Input size N is lg(A) + lg(B)
S is O(N)

所以迭代次数在输入位数上是线性的。对于适合于CPU寄存器的数字,可以将迭代建模为占用恒定时间,并假设GCD的总运行时间是线性的。

当然,如果处理的是大整数,那么必须考虑到每个迭代中的模运算没有固定的成本。粗略地说,总的渐近运行时间是多对数因子的n^2倍。有点像n^2 lg(n) 2^O(log* n)。使用二进制GCD可以避免使用多对数因子。


分析算法的合适方法是确定其最坏情况。欧几里得GCD的最坏情况发生在涉及斐波那契对时。void EGCD(fib[i], fib[i - 1]),其中i>0。

例如,让我们选择股息是55,除数是34的情况(记住,我们仍在处理斐波那契数)。

enter image description here

正如您可能注意到的,这个操作花费了8次迭代(或递归调用)。

让我们试试更大的斐波那契数,即121393和75025。我们在这里也可以注意到它花费了24次迭代(或递归调用)。

enter image description here

您还可以注意到,每个迭代都会产生一个斐波那契数。所以我们有这么多的行动。我们不能只用斐波那契数来获得类似的结果。

因此,时间复杂性将由小的oh(上界)表示,这次。下界是直观的ω(1):例如500除以2。

我们来解递推关系:

enter image description here

我们可以说,欧几里德GCD最多可以进行log(XY)操作。


在维基百科的文章中有一个很好的视角。

对于值对,它甚至有一个很好的复杂度图。

不是O(a%b)

众所周知,它所采取的步骤永远不会超过较小数字位数的五倍。因此,最大步数随着位数的增加而增加。每一步的成本也随着位数的增加而增加,因此复杂性受O(ln^2 b)的约束,其中b是较小的数字。这是一个上限,实际时间通常比较短。


请看这里。

尤其是本部分:

Lamé showed that the number of steps needed to arrive at the greatest common divisor for two numbers less than n is

alt text

因此,O(log min(a, b))是一个很好的上界。


这是对欧几里得算法运行时复杂性的直观理解。正式的证明包含在各种文本中,如算法介绍和TAOCP第2卷。

首先考虑一下,如果我们尝试用两个斐波那契数f(k+1)和f(k)的gcd。您可能会很快观察到欧几里得算法迭代到f(k)和f(k-1)。也就是说,每次迭代,我们在斐波那契级数中向下移动一个数字。由于斐波那契数是O(phi^k),其中phi是黄金比率,我们可以看到gcd的运行时间是O(log n),其中n=max(a,b),logs以phi为基数。接下来,我们可以通过观察斐波那契数持续产生对来证明这是最坏的情况,其中余数在每次迭代中保持足够大,并且直到到达系列的开始时才变为零。

我们可以使o(log n)中n=max(a,b)的界限更紧。假设b>=a,这样我们就可以在o(logb)处写入绑定。首先,观察gcd(ka,kb)=gcd(a,b)。由于k的最大值是gcd(a,c),我们可以在运行时用b/gcd(a,b)替换b,从而使o的界限更为严格(logb/gcd(a,b))。


欧几里德算法的最坏情况是,在每一步中,余数是最大的,即对于斐波那契序列的两个连续项。

当n和m是a和b的位数时,假设n大于等于m,则算法使用o(m)除法。

请注意,复杂性总是根据输入的大小给出的,在这种情况下是数字的数量。


当n和m都是连续的斐波那契数时,会出现最坏的情况。

GN(FN,FN?1)=GCD(FN?1,FN?2)=?=gcd(f1,f0)=1,第n个fibonacci数为1.618^n,其中1.618是黄金比率。

因此,为了找到gcd(n,m),递归调用的数量将是完成(logn)。


然而,对于迭代算法,我们有:

1
2
3
4
5
6
7
8
9
10
11
12
13
int iterativeEGCD(long long n, long long m) {
    long long a;
    int numberOfIterations = 0;
    while ( n != 0 ) {
         a = m;
         m = n;
         n = a % n;
        numberOfIterations ++;
    }
    printf("
Iterative GCD iterated %d times.", numberOfIterations);
    return m;
}

对于fibonacci对,iterativeEGCD()iterativeEGCDForWorstCase()之间没有区别,后者如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
int iterativeEGCDForWorstCase(long long n, long long m) {
    long long a;
    int numberOfIterations = 0;
    while ( n != 0 ) {
         a = m;
         m = n;
         n = a - n;
        numberOfIterations ++;
    }
    printf("
Iterative GCD iterated %d times.", numberOfIterations);
    return m;
}

是的,对于斐波那契对,n = a % nn = a - n,情况完全一样。

我们也知道,在之前对同一问题的回答中,有一个主要的递减因素:factor = m / (n % m)

因此,为了以定义的形式形成欧几里得GCD的迭代版本,我们可以将其描述为"模拟器",如下所示:

1
2
3
4
5
6
7
8
9
10
void iterativeGCDSimulator(long long x, long long y) {
    long long i;
    double factor = x / (double)(x % y);
    int numberOfIterations = 0;
    for ( i = x * y ; i >= 1 ; i = i / factor) {
        numberOfIterations ++;
    }
    printf("
Iterative GCD Simulator iterated %d times.", numberOfIterations);
}

根据Jauhar Ali博士的工作(最后一张幻灯片),上面的循环是对数的。

enter image description here

是的,小哦,因为模拟器最多能告诉迭代次数。在欧几里得GCD上进行研究时,非斐波那契对的迭代次数比斐波那契少。


Gabriel-Lame定理用对数(1/sqrt(5)*(a+1/2))-2限制步数,其中对数的基数是(1+sqrt(5))/2。这是针对算法的最坏情况场景,当输入是连续的Fibanocci数时会发生。

稍微宽松一点的界限是:log a,其中日志的基(sqrt(2))由koblitz隐含。

出于密码的目的,我们通常考虑算法的位复杂性,考虑到比特大小近似由k=Loga给出。

下面是Euclid Algorith的位复杂度的详细分析:

虽然在大多数文献中,O(Loga)^ 3给出欧几里德算法的位复杂度,但存在一个更严格的界限,即O(Loga)^ 2。

考虑;r0=a,r1=b,r0=q1.r1+r2。…,ri-1=qi.ri+ri+1,……,rm-2=qm-1.rm-1+rm rm-1=qm.rm

观察:a=r0>=b=r1>r2>r3…>rm-1>rm>0………(1)

Rm是a和b的最大公约数。

根据Koblitz的书(数论和密码学课程)中的一个主张,可以证明:ri+1<(ri-1)/2……………(2)

在Koblitz中,将k位正整数除以l位正整数所需的位操作数(假设k>=l)被给出为:(k-l+1).l………(3)

通过(1)和(2)除数是O(loga),因此通过(3)总的复杂性是O(loga)^3。

现在,这可能会减少到O(loga)^2在koblitz的评论。

考虑Ki=logri+1

通过(1)和(2)我们得到:对于i=0,1,…,m-2,m-1和ki+2<=(ki)-1对于i=0,1,…,m-2

并且由(3)m除数的总成本以:和[(ki-1)-((ki-1))]*ki为界,i=0,1,2,…,m

重新排列:总和[(ki-1)-((ki-1))]*ki<=4*k0^2

所以欧几里得算法的位复杂性是O(loga)^2。