유클리드 알고리즘의 시간 복잡성


97

Euclid의 최대 공통 분모 알고리즘의 시간 복잡도를 결정하는 데 어려움이 있습니다. 의사 코드의이 알고리즘은 다음과 같습니다.

function gcd(a, b)
    while b ≠ 0
       t := b
       b := a mod b
       a := t
    return a

ab 에 의존하는 것 같습니다 . 내 생각은 시간 복잡도가 O (a % b)라는 것입니다. 그 맞습니까? 그것을 작성하는 더 좋은 방법이 있습니까?


14
Knuth TAOCP, Volume 2를 참조하십시오 . 그는 광범위한 범위 를 제공합니다 . FWIW, 몇 가지 간단한 정보 : a%b. 때 최악의 경우는 ab연속 피보나치 숫자이다.
Jerry Coffin

3
@JerryCoffin 참고 : 더 공식적인 방식으로 최악의 경우가 실제로 피보나치 수임을 증명하려면 종료하기 전에 n 번째 단계가 수학적 유도를 통해 n 번째 피보나치 수의 gcd를 곱한 값 이상이어야한다는 것을 증명하는 것이 좋습니다.
Mygod

답변:


73

Euclid 알고리즘의 시간 복잡성을 분석하는 한 가지 트릭은 두 번의 반복에서 발생하는 일을 따르는 것입니다.

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

이제 a와 b가 모두 감소하므로 분석이 더 쉬워집니다. 케이스로 나눌 수 있습니다.

  • 작은 A : 2a <= b
  • 작은 B : 2b <= a
  • 작은 A : 2a > b하지만a < b
  • 작은 B : 2b > a하지만b < a
  • 같은: a == b

이제 모든 단일 케이스가 합계 a+b를 최소 1/4 만큼 감소시키는 것을 보여줍니다 .

  • Tiny A : b % (a % b) < a2a <= b, 그래서 b최소한 절반으로 a+b감소 하므로 최소한25%
  • Tiny B : a % b < b2b <= a, 그래서 a최소한 절반으로 a+b감소 하므로 최소한25%
  • 작은 A는 : b될 것입니다 b-a보다 작은, b/2감소, a+b최소한으로 25%.
  • 작은 B는 : a될 것입니다 a-b보다 작은, a/2감소, a+b최소한으로 25%.
  • 같음 :로 a+b떨어집니다 0. 이는 분명히 a+b적어도 25%.

따라서 사례 분석에 따르면 모든 이중 단계 a+b는 최소한 25%. a+b아래로 떨어질 때까지이 문제가 발생할 수있는 최대 횟수가 있습니다 1. S0에 도달 할 때까지 의 총 단계 수 ( )는을 충족해야합니다 (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 배가 될 것입니다. 같은 것 n^2 lg(n) 2^O(log* n) . 대신 이진 gcd 를 사용하여 다대수 인자를 피할 수 있습니다 .


왜 "b % (a % b) <a"인지 설명해 주시겠습니까?
Michael Heidelberg

3
@MichaelHeidelberg x % y는보다 클 수 없으며보다 작아야 x합니다 y. 그래서 a % b기껏해야 a, 기껏 해야 b % (a%b)어떤 것보다 아래에 있어야 a하므로 전체적으로 더 작은 것 a입니다.
Craig Gidney

@ Cheersandhth.-Alf 선호하는 용어의 약간의 차이가 "심각하게 틀렸다"고 생각하십니까? 물론 CS 용어를 사용했습니다. 컴퓨터 과학 문제입니다. 그럼에도 불구하고 나는 "자릿수"라고 대답을 명확히했다.
Craig Gidney

@CraigGidney : 고쳐 주셔서 감사합니다. 이제 저는 순수 학자들이 작성한 많은 위키 백과 기사에서 의사 소통 문제를 인식합니다. 이것을 고려하십시오 : 내 의견에서했던 것처럼 O (log (min (a, b))를 작성하는 대신 자릿수에 대해 이야기하는 주된 이유는 수학적이 아닌 사람들이 이해하기 쉽게 만드는 것입니다. 걱정은 그냥 "로그"등을 쓰세요. 자릿수 의 목적 이 문제가있는 사람들을 돕는 것입니다. 이 개념의 이름을 "크기"로 지정하고 정의를 다른 곳에두고 "로그"에 대해 이야기하지 마십시오. 끝, 당신은 도움 대신 모호합니다
건배와 hth – Alf aug

마지막 단락이 잘못되었습니다. 관련 텔레 스코핑 시리즈를 합하면 교과서 2 차 시분할 알고리즘을 사용하더라도 시간 복잡도가 O (n ^ 2)에 불과하다는 것을 알 수 있습니다.
Emil Jeřábek

27

알고리즘을 분석하는 적절한 방법은 최악의 시나리오를 결정하는 것입니다. 유클리드 GCD의 최악의 경우는 피보나치 쌍이 관련 될 때 발생합니다. void EGCD(fib[i], fib[i - 1]), 여기서 i> 0.

예를 들어, 배당이 55이고 제수가 34 인 경우를 선택해 봅시다 (우리는 여전히 피보나치 수를 처리하고 있음을 기억하십시오).

여기에 이미지 설명 입력

아시다시피이 작업에는 8 번의 반복 (또는 재귀 호출)이 필요했습니다.

더 큰 피보나치 수, 즉 121393과 75025를 시도해 봅시다. 여기에서도 24 번의 반복 (또는 재귀 호출)이 필요하다는 것을 알 수 있습니다.

여기에 이미지 설명 입력

또한 각 반복이 피보나치 수를 산출한다는 것을 알 수 있습니다. 그것이 우리가 많은 작업을하는 이유입니다. 실제로 피보나치 수만으로는 비슷한 결과를 얻을 수 없습니다.

따라서 시간 복잡도는 이번에는 작은 Oh (상한선)로 표시 될 것입니다. 하한은 직관적으로 Omega (1)입니다. 예를 들어 500을 2로 나눈 경우입니다.

반복 관계를 해결해 보겠습니다.

여기에 이미지 설명 입력

그러면 유클리드 GCD가 최대 log (xy) 연산 할 수 있다고 말할 수 있습니다 .


2
나는이 분석이 틀렸다고 생각한다. 왜냐하면베이스가 입력에 의존하기 때문이다.
HopefullyHelpful

종속 염기가 문제를 나타낸다는 것을 증명할 수 있습니까?
Mohamed Ennahdi El Idrissi

1
베이스는 분명히 황금 비율입니다. 왜? nod (13,8) 대 nod (8,5)를 계산하는 데 정확히 한 단계가 더 필요하기 때문입니다. 고정 x의 경우 y <x 인 경우 최악의 성능은 x = fib (n + 1), y = fib (n)입니다. 여기서 y는 x에 의존하므로 x 만 볼 수 있습니다.
Stepan

17

위키피디아 기사 에서 이것에 대한 훌륭한 모습이 있습니다.

값 쌍에 대한 복잡한 복잡성도 있습니다.

그렇지 않습니다 O(a%b).

더 작은 숫자의 자릿수보다 5 배 이상 더 많은 단계를 거치지 않는 것으로 알려져 있습니다 (기사 참조). 따라서 최대 단계 수는 자릿수에 따라 증가합니다 (ln b). 각 단계의 비용은 자릿수에 따라 증가하므로 복잡성은 O(ln^2 b)b가 더 작은 수에 의해 제한됩니다 . 이는 상한이며 실제 시간은 일반적으로 더 적습니다.


무엇을 n상징합니까?
IVlad 2010 년

@IVlad : 자릿수. 대답을 명확히했습니다. 감사합니다.
JoshD 2010 년

OP의 알고리즘의 경우 (큰 정수) 나누기 (빼기가 아님)를 사용하는 것은 실제로 O (n ^ 2 log ^ 2n)와 비슷합니다.
Alexandre C.

@Alexandre C .: 명심하십시오 n = ln b. big int에 대한 모듈러스의 규칙적인 복잡성은 무엇입니까? O (log n log ^ 2 log n)
JoshD

@JoshD : 그것은 그런 것입니다. 나는 log n 항을 놓친 것 같습니다.이 경우 최종 복잡성 (나눗셈이있는 알고리즘의 경우)은 O (n ^ 2 log ^ 2 n log n)입니다.
Alexandre C.

13

를 참조하십시오 여기 .

특히이 부분 :

Lamé는 n보다 작은 두 수에 대해 최대 공약수에 도달하는 데 필요한 단계의 수는 다음과 같습니다.

대체 텍스트

따라서 O(log min(a, b))좋은 상한선입니다.


3
이는 단계 수에 해당되지만 자릿수 (ln n)에 따라 확장되는 각 단계 자체의 복잡성을 고려하지 않습니다.
JoshD

9

다음은 Euclid 알고리즘의 런타임 복잡성에 대한 직관적 인 이해입니다. 공식 증명은 알고리즘 소개 및 TAOCP Vol 2와 같은 다양한 텍스트에서 다루어집니다.

먼저 두 개의 피보나치 수 F (k + 1) 및 F (k)의 gcd를 취하려고 시도하면 어떻게 될지 생각해보십시오. Euclid의 알고리즘이 F (k) 및 F (k-1)까지 반복되는 것을 빠르게 관찰 할 수 있습니다. 즉, 반복 할 때마다 피보나치 시리즈에서 한 숫자 아래로 이동합니다. 피보나치 수는 Phi가 황금비 인 O (Phi ^ k)이므로 GCD의 실행 시간은 O (log n)이고 여기서 n = max (a, b)이고 log는 Phi의 밑수를가집니다. 다음으로, 피보나치 수는 각 반복에서 충분히 큰 나머지가 연속으로 시작될 때까지 0이되지 않는 쌍을 일관되게 생성한다는 것을 관찰함으로써 이것이 최악의 경우임을 증명할 수 있습니다.

n = max (a, b) 바인딩 된 O (log n)를 더욱 단단하게 만들 수 있습니다. b> = a라고 가정하면 O (log b)에 바운드를 쓸 수 있습니다. 먼저 GCD (ka, kb) = GCD (a, b)를 관찰하십시오. k의 가장 큰 값은 gcd (a, c)이므로 런타임에서 b를 b / gcd (a, b)로 대체하여 O (log b / gcd (a, b))의 경계를 더 좁힐 수 있습니다.


Fibonacci nos가 Euclids algo에 대해 최악의 경우를 생성한다는 공식적인 증거를 제공 할 수 있습니까?
Akash

4

Euclid 알고리즘의 최악의 경우는 나머지가 각 단계에서 가능한 가장 큰 경우입니다. 피보나치 수열의 두 연속 항에 대해.

n과 m이 a와 b의 자릿수 일 때 n> = m이라고 가정하면 알고리즘은 O (m) 나눗셈을 사용합니다.

복잡성은 항상 입력 크기 ,이 경우 자릿수 측면에서 제공됩니다.


4

n과 m이 모두 연속적인 피보나치 수일 때 최악의 경우가 발생합니다.

gcd (Fn, Fn−1) = gcd (Fn−1, Fn−2) = ⋯ = gcd (F1, F0) = 1이고 n 번째 피보나치 수는 1.618 ^ n이며, 여기서 1.618은 황금 비율입니다.

따라서 gcd (n, m)을 찾으려면 재귀 호출 수는 Θ (logn)이됩니다.


3

여기 책의 분석 인 C의 데이터 구조와 알고리즘을 분석 하여 마크 알렌 웨이스 (2 판, 2.4.4)

Euclid의 알고리즘은 0에 도달 할 때까지 나머지를 계속 계산하여 작동합니다. 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;
}

다음은 우리가 사용할 이론 입니다.

만일 M> N, 다음 M 개조 N <M / 2.

증명:

두 가지 경우가 있습니다. N <= M / 2이면 나머지가 N보다 작기 때문에이 경우 정리가 참입니다. 다른 경우는 N> M / 2입니다. 그러나 N은 나머지 M-N <M / 2와 함께 한 번 M에 들어가 정리를 증명합니다.

따라서 다음과 같은 추론을 할 수 있습니다.

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이면 루프의 첫 번째 반복이이를 바꿉니다).


2

Gabriel Lame의 정리는 log (1 / sqrt (5) * (a + 1 / 2))-2로 단계 수를 제한합니다. 여기서 로그의 밑은 (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 ...> rm-1> rm> 0 .......... (1)

rm은 a와 b의 최대 공약수입니다.

Koblitz의 저서 (A course in number Theory and Cryptography)의 주장으로 다음과 같이 증명할 수 있습니다. ri + 1 <(ri-1) / 2 ................. ( 2)

다시 Koblitz에서 k 비트 양의 정수를 l 비트 양의 정수 (k> = l 가정)로 나누는 데 필요한 비트 연산의 수는 다음과 같이 지정됩니다. (k-l + 1) .l ...... .............(삼)

(1)과 (2)의 분할 수는 O (loga)이므로 (3)의 총 복잡도는 O (loga) ^ 3입니다.

이제 이것은 Koblitz의 발언에 의해 O (loga) ^ 2로 축소 될 수 있습니다.

ki = logri +1 고려

(1)과 (2)에 의해 우리는 i = 0,1, ..., m-2, m-1의 경우 ki + 1 <= ki, i = 0의 경우 ki + 2 <= (ki) -1 , 1, ..., m-2

(3) m 디비전의 총 비용은 다음과 같이 제한됩니다. SUM [(ki-1)-((ki) -1))] * ki for i = 0,1,2, .., m

재정렬하기 : SUM [(ki-1)-((ki) -1))] * ki <= 4 * k0 ^ 2

따라서 Euclid 알고리즘의 비트 복잡도는 O (loga) ^ 2입니다.


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;
}

피보나치 쌍을 사용 하면 후자가 다음과 같이 보이는 위치 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;
}

예, 피보나치 쌍 n = a % nn = a - n,, 정확히 같은 것입니다.

우리는 또한 같은 질문에 대한 이전 답변에서 우세한 감소 요인이 있음을 알고 factor = m / (n % m)있습니다.

따라서 정의 된 형식으로 Euclidean 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 박사 의 작업 (마지막 슬라이드)에 따르면 위의 루프는 로그입니다.

여기에 이미지 설명 입력

네, 작습니다. 시뮬레이터가 최대 반복 횟수 알려주기 때문 입니다. 비 피보나치 쌍은 유클리드 GCD에서 조사 할 때 피보나치보다 반복 횟수가 적습니다.


이 연구는 C 언어를 사용하여 수행 되었기 때문에 정밀도 문제로 인해 잘못된 / 부정확 한 값이 생성 될 수 있습니다. 그래서 long long 을 사용하여 factor 라는 부동 소수점 변수에 더 적합합니다 . 사용 된 컴파일러는 MinGW 2.95입니다.
Mohamed Ennahdi El Idrissi

1

모든 단계에서 두 가지 경우가 있습니다.

b> = a / 2, a, b = b, a % b는 b를 이전 값의 최대 절반으로 만듭니다.

b <a / 2이면 a, b = b, a % b는 b가 a / 2보다 작기 때문에 이전 값의 절반 이하로 a를 만듭니다.

따라서 모든 단계에서 알고리즘은 최소 하나의 숫자를 최소 절반으로 줄입니다.

최대 O (log a) + O (log b) 단계에서 이것은 단순한 경우로 축소됩니다. O (log n) 알고리즘을 생성합니다. 여기서 n은 a와 b의 상한입니다.

여기 에서 찾았습니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.