我很难确定Euclid最大公分母算法的时间复杂度。伪代码中的这种算法是:
function gcd(a, b)
while b ≠ 0
t := b
b := a mod b
a := t
return a
似乎依赖于 a 和 b 。我的想法是时间复杂度是O(a%b)。那是对的吗?有没有更好的方法来写这个?
答案 0 :(得分:63)
分析Euclid算法的时间复杂度的一个技巧是遵循两次迭代中发生的事情:
a', b' := a % b, b % (a % b)
现在a和b都会减少,而不是只减少一个,这使得分析更容易。你可以把它分成几个案例:
2a <= b
2b <= a
2a > b
,但a < b
2b > a
但b < a
a == b
现在我们将展示每一个案例将总a+b
减少至少四分之一:
b % (a % b) < a
和2a <= b
,因此b
减少了至少一半,因此a+b
减少了至少25%
a % b < b
和2b <= a
,因此a
减少了至少一半,因此a+b
减少了至少25%
b
将变为b-a
,小于b/2
,a+b
至少减少25%
。a
将变为a-b
,小于a/2
,a+b
至少减少25%
。a+b
降至0
,明显降低a+b
至少25%
。因此,通过案例分析,每两步减少a+b
至少25%
。在a+b
被迫降至1
以下之前,最多可能发生这种情况。在我们达到0之前的步骤总数(S
)必须满足(4/3)^S <= A+B
。现在就开始吧:
(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倍。 Something like n^2 lg(n) 2^O(log* n)
。相反,使用binary gcd可以避免多对数因子。
答案 1 :(得分:24)
分析算法的合适方法是确定最坏情况。
Euclidean GCD的最坏情况发生在涉及Fibonacci对时。
void EGCD(fib[i], fib[i - 1])
,其中i&gt; 0
例如,让我们选择股息为55,除数为34的情况(回想一下我们仍在处理斐波那契数字)。
正如您可能注意到的,此操作花费了8次迭代(或递归调用)。
让我们尝试更大的斐波纳契数,即121393和75025.我们也可以注意到它需要24次迭代(或递归调用)。
您还可以注意到每次迭代都会产生斐波纳契数。这就是我们有这么多业务的原因。我们不能仅用Fibonacci数字获得类似的结果。
因此,时间复杂度将由小Oh(上限)表示,这次。下限是直观的Omega(1):例如500除以2的情况。
让我们解决递归关系:
我们可以说欧几里德GCD可以使log(xy)操作最多。
答案 2 :(得分:17)
在wikipedia article上可以看到这一点。
它甚至可以为价值对提供一个很好的复杂图。
不是O(a%b)
。
众所周知(参见文章),它将永远不会超过较小数字中数字位数的五倍。因此,最大步数随着数字(ln b)
的增长而增长。每个步骤的成本也随着数字的增加而增长,因此复杂性受O(ln^2 b)
的约束,其中b是较小的数字。这是一个上限,实际时间通常较少。
答案 3 :(得分:11)
答案 4 :(得分:8)
这是对Euclid算法的运行时复杂性的直观理解。正式证明包含在各种文本中,例如算法导论和TAOCP第2卷。
首先想想如果我们试图取两个Fibonacci数F(k + 1)和F(k)的gcd。你可能很快就会发现Euclid的算法迭代到F(k)和F(k-1)。也就是说,每次迭代我们都会在Fibonacci系列中向下移动一个数字。由于Fibonacci数是O(Phi ^ k),其中Phi是黄金比率,我们可以看到GCD的运行时间是O(log n),其中n = max(a,b)并且log具有Phi的基数。接下来,我们可以证明这是最糟糕的情况,通过观察Fibonacci数始终产生对,其中剩余部分在每次迭代中保持足够大,并且在你到达系列的开始之前永远不会变为零。
我们可以使O(log n)n = max(a,b)更加紧密。假设b> = a,我们可以在O(log b)处写入绑定。首先,观察GCD(ka,kb)= GCD(a,b)。由于k的最大值是gcd(a,c),我们可以在运行时用b / gcd(a,b)替换b,从而导致更严格的O界限(log b / gcd(a,b))。
答案 5 :(得分:4)
欧几里德算法的最坏情况是剩余部分在每一步都是最大的,即。连续两个Fibonacci序列。
当n和m是a和b的位数时,假设n> = m,算法使用O(m)除法。
请注意,复杂性始终以输入的尺寸表示,在本例中为数字位数。
答案 6 :(得分:2)
当n和m都是连续的Fibonacci数时,会出现最坏的情况。
gcd(Fn,Fn-1)= gcd(Fn-1,Fn-2)=⋯= gcd(F1,F0)= 1且第n个斐波纳契数是1.618 ^ n,其中1.618是黄金比率。< / p>
因此,要找到gcd(n,m),递归调用的数量将为Θ(logn)。
答案 7 :(得分:1)
然而,对于迭代算法,我们有:
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("\nIterative GCD iterated %d times.", numberOfIterations);
return m;
}
使用Fibonacci对,iterativeEGCD()
和iterativeEGCDForWorstCase()
之间没有区别,后者如下所示:
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("\nIterative GCD iterated %d times.", numberOfIterations);
return m;
}
是的,对于Fibonacci Pairs,n = a % n
和n = a - n
,这是完全相同的。
我们也知道,在同一问题的早期回复中,存在一个普遍的减少因素:factor = m / (n % m)
。
因此,为了以定义的形式塑造欧几里德GCD的迭代版本,我们可能会将其描述为“模拟器”:
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("\nIterative GCD Simulator iterated %d times.", numberOfIterations);
}
基于Jauhar Ali博士的work(最后一张幻灯片),上面的循环是对数的。
是的,小哦,因为模拟器告诉迭代次数最多。在欧几里德GCD上探测时,非斐波纳契对的迭代次数比斐波那契少。
答案 8 :(得分:0)
Gabriel Lame的定理按log(1 / sqrt(5)*(a + 1/2)) - 2限制步数,其中log的基数为(1 + sqrt(5))/ 2。这是针对算法的最坏情况场景,当输入是连续的Fibanocci数时就会发生。
稍微宽松的界限是:log a,其中日志的基数是(sqrt(2))由Koblitz暗示。
出于加密目的,我们通常会考虑算法的按位复杂性,同时考虑到位大小约为k = loga。
这是对Euclid算法的按位复杂性的详细分析:
虽然在大多数参考文献中,欧几里德算法的按位复杂度由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 ...&gt; rm-1> rm> 0 ..........(1)
和rm是a和b的最大公约数。
Koblitz的书(理论与密码学课程)中的一篇论文可以证明:ri + 1&lt;(ri-1)/ 2 ............... ..(2)
再次在Koblitz中,将k位正整数除以1位正整数所需的位操作数(假设k> = 1)如下:(k-1 + 1).l ... .....................(3)
通过(1)和(2),除数的数量为O(loga),因此(3)总复杂度为O(loga)^ 3.
现在,这可以通过Koblitz中的一句话减少到O(loga)^ 2。
考虑ki = logri +1
由(1)和(2)得到:ki + 1&lt; = ki,i = 0,1,...,m-2,m-1和ki + 2 <=(ki)-1 I = 0,1,...,M-2
和(3)m除数的总成本受以下因素限制:SUM [(ki-1) - ((ki)-1))] * ki,i = 0,1,2,..,米
重新排列:SUM [(ki-1) - ((ki)-1))] * ki <= 4 * k0 ^ 2
因此欧几里德算法的按位复杂度为O(loga)^ 2。
答案 9 :(得分:0)
这是 Mark Allen Weiss (第二版,2.4.4)在 C中的数据结构和算法分析 中的分析。 :
Euclid的算法通过连续计算余数直到达到0来工作。最后的非零余数就是答案。
代码如下:
unsigned int Gcd(unsigned int M, unsigned int N)
{
unsigned int Rem;
while (N > 0) {
Rem = M % N;
M = N;
N = Rem;
}
Return M;
}
这是我们要使用的 THEOREM :
如果 M> N,则然后 M mod N
证明:
有两种情况。如果N <= M / 2,则由于余数较小 比N的定理在这种情况下成立。另一种情况是N> M / 2。 但随后N进入M一次,余数为M-N
因此,我们可以进行以下推断:
Variables M N Rem
initial M N M%N
1 iteration N M%N N%(M%N)
2 iterations M%N N%(M%N) (M%N)%(N%(M%N)) < (M%N)/2
因此,经过两次迭代,其余部分最多为原始值的一半。这将表明迭代次数最多为
2logN = O(logN)
。请注意,假设M> = N,该算法将计算Gcd(M,N)(如果N> M,则循环的第一次迭代会交换它们。)
答案 10 :(得分:0)
每一步都有两种情况
b> = a / 2,然后a,b = b,a%b将使b最多为其先前值的一半
b
因此,在每一步中,该算法都会将至少一个数字减少到至少减少一半。 在最多 O(log a)+ O(log b)步骤中,这将简化为简单情况。得出O(log n)算法,其中n是a和b的上限。 我找到了here