Độ phức tạp về thời gian của Thuật toán Euclid


97

Tôi đang gặp khó khăn trong việc quyết định độ phức tạp về thời gian của thuật toán mẫu số chung lớn nhất của Euclid là bao nhiêu. Thuật toán này trong mã giả là:

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

Nó dường như phụ thuộc vào ab . Suy nghĩ của tôi là độ phức tạp về thời gian là O (a% b). Đúng không? Có cách nào tốt hơn để viết điều đó không?


14
Xem Knuth TAOCP, Tập 2 - anh ấy đưa ra phạm vi bao quát . Chỉ FWIW, một vài mẩu tin nhỏ: nó không tỷ lệ thuận với a%b. Trường hợp xấu nhất là khi ablà các số Fibonacci liên tiếp.
Jerry Coffin vào

3
@JerryCoffin Lưu ý: Nếu bạn muốn chứng minh trường hợp xấu nhất thực sự là số Fibonacci theo cách chính thức hơn, hãy xem xét việc chứng minh bước thứ n trước khi kết thúc ít nhất phải lớn bằng gcd nhân với số Fibonacci thứ n bằng quy nạp toán học.
Mygod

Câu trả lời:


73

Một mẹo để phân tích độ phức tạp về thời gian của thuật toán Euclid là theo dõi những gì xảy ra qua hai lần lặp:

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

Bây giờ cả a và b sẽ giảm xuống, thay vì chỉ một, điều này làm cho việc phân tích dễ dàng hơn. Bạn có thể chia nó thành các trường hợp:

  • Tiny A: 2a <= b
  • Tiny B: 2b <= a
  • Nhỏ A: 2a > bnhưnga < b
  • Nhỏ B: 2b > anhưngb < a
  • Công bằng: a == b

Bây giờ, chúng tôi sẽ chỉ ra rằng mỗi trường hợp đơn lẻ sẽ giảm tổng a+bít nhất một phần tư:

  • Tiny A: b % (a % b) < a2a <= b, do đó, bgiảm ít nhất một nửa, vì vậy a+bgiảm ít nhất25%
  • Tiny B: a % b < b2b <= a, agiảm ít nhất một nửa, vì vậy a+bgiảm ít nhất25%
  • A nhỏ: bsẽ trở thành b-a, nhỏ hơn b/2, giảm a+bít nhất 25%.
  • B nhỏ: asẽ trở thành a-b, nhỏ hơn a/2, giảm a+bít nhất 25%.
  • Bằng: a+bgiảm xuống 0, rõ ràng là giảm a+bít nhất 25%.

Do đó, theo phân tích trường hợp, mỗi bước kép giảm a+bít nhất 25%. Có một số lần tối đa điều này có thể xảy ra trước khi a+bbuộc phải giảm xuống dưới 1. Tổng số bước ( S) cho đến khi chúng ta chạm 0 phải thỏa mãn (4/3)^S <= A+B. Bây giờ chỉ cần làm việc đó:

(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)

Vì vậy số lần lặp là tuyến tính theo số chữ số đầu vào. Đối với các số vừa với thanh ghi cpu, sẽ hợp lý khi lập mô hình lặp lại theo thời gian không đổi và giả sử rằng tổng thời gian chạy của gcd là tuyến tính.

Tất nhiên, nếu bạn đang xử lý các số nguyên lớn, bạn phải tính đến thực tế là các hoạt động mô đun trong mỗi lần lặp không có chi phí cố định. Nói một cách đơn giản, tổng thời gian chạy tiệm cận sẽ gấp n ^ 2 lần một hệ số đa thức. Một cái gì đó giống như n^2 lg(n) 2^O(log* n) . Có thể tránh yếu tố đa thức bằng cách sử dụng gcd nhị phân .


Bạn có thể giải thích tại sao "b% (a% b) <a" được không?
Michael Heidelberg

3
@MichaelHeidelberg x % ykhông được nhiều hơn xvà phải nhỏ hơn y. Tối đa a % blà như vậy a, buộc b % (a%b)phải thấp hơn một cái gì đó nhiều nhất avà do đó nói chung là thấp hơn a.
Craig Gidney

@ Cheersandhth.-Alf Bạn cho rằng một sự khác biệt nhỏ trong thuật ngữ ưa thích là "sai nghiêm trọng"? Tất nhiên tôi đã sử dụng thuật ngữ CS; đó là một câu hỏi về khoa học máy tính. Bất kể, tôi đã làm rõ câu trả lời để nói "số chữ số".
Craig Gidney

@CraigGidney: Cảm ơn bạn đã sửa lỗi đó. Bây giờ tôi nhận ra vấn đề giao tiếp từ nhiều bài báo Wikipedia được viết bởi các học giả thuần túy. Hãy xem xét điều này: lý do chính để nói về số chữ số, thay vì chỉ viết O (log (min (a, b)) như tôi đã làm trong nhận xét của mình, là để làm cho mọi thứ dễ hiểu hơn đối với những người không chuyên về toán học. Không có điều đó mối quan tâm chỉ cần viết "nhật ký", v.v. Vì vậy, đó là mục đích của số chữ số, giúp những người bị thách thức. Khi bạn đặt tên cho khái niệm này là "kích thước", và có định nghĩa ở nơi khác, và không nói về "nhật ký" tại kết thúc, bạn che khuất thay vì giúp đỡ.
Cheers and hth. - Alf

Đoạn cuối không chính xác. Nếu bạn tính tổng các chuỗi điều chỉnh có liên quan, bạn sẽ thấy rằng độ phức tạp về thời gian chỉ là O (n ^ 2), ngay cả khi bạn sử dụng thuật toán chia thời gian bậc hai trong sách học.
Emil Jeřábek

27

Cách thích hợp để phân tích một thuật toán là xác định các tình huống xấu nhất của nó. Trường hợp xấu nhất của Euclidean GCD xảy ra khi có các cặp Fibonacci. void EGCD(fib[i], fib[i - 1]), trong đó i> 0.

Ví dụ: hãy chọn trường hợp số bị chia là 55 và số chia là 34 (nhớ lại rằng chúng ta vẫn đang xử lý các số fibonacci).

nhập mô tả hình ảnh ở đây

Như bạn có thể nhận thấy, thao tác này tốn 8 lần lặp (hoặc gọi đệ quy).

Hãy thử các số Fibonacci lớn hơn, cụ thể là 121393 và 75025. Chúng ta cũng có thể nhận thấy ở đây rằng nó mất 24 lần lặp (hoặc gọi đệ quy).

nhập mô tả hình ảnh ở đây

Bạn cũng có thể nhận thấy rằng mỗi lần lặp lại tạo ra một số Fibonacci. Đó là lý do tại sao chúng tôi có rất nhiều hoạt động. Chúng tôi thực sự không thể thu được kết quả tương tự chỉ với số Fibonacci.

Do đó, độ phức tạp về thời gian sẽ được biểu thị bằng Oh nhỏ (giới hạn trên), lần này. Giới hạn dưới trực quan là Omega (1): ví dụ: trường hợp 500 chia cho 2.

Hãy giải quyết mối quan hệ lặp lại:

nhập mô tả hình ảnh ở đây

Khi đó chúng ta có thể nói rằng Euclidean GCD có thể thực hiện phép toán log (xy) nhiều nhất .


2
Tôi nghĩ rằng phân tích này là sai, bởi vì cơ sở là phụ thuộc và đầu vào.
HopefullyHelpful

Bạn có thể chứng minh rằng một cơ sở phụ thuộc đại diện cho một vấn đề không?
Mohamed Ennahdi El Idrissi

1
Cơ sở là tỷ lệ vàng hiển nhiên. Tại sao? Bởi vì phải mất thêm một bước nữa để tính gật đầu (13,8) so với gật đầu (8,5). Đối với x cố định nếu y <x, hiệu suất trong trường hợp xấu nhất là x = fib (n + 1), y = fib (n). Ở đây y phụ thuộc vào x, vì vậy chúng ta chỉ có thể xem xét x.
Stepan

17

Có một cái nhìn tuyệt vời về điều này trên bài báo wikipedia .

Nó thậm chí còn có một âm mưu phức tạp tốt đẹp cho các cặp giá trị.

Nó không phải O(a%b).

Được biết (xem bài viết) rằng nó sẽ không bao giờ thực hiện nhiều bước hơn năm lần số chữ số trong số nhỏ hơn. Vì vậy, số bước tối đa tăng lên khi số chữ số (ln b). Chi phí của mỗi bước cũng tăng lên khi số chữ số, do đó, độ phức tạp bị ràng buộc bởi O(ln^2 b)trong đó b là số nhỏ hơn. Đó là giới hạn trên và thời gian thực tế thường ít hơn.


Không gì nđại diện?
IVlad

@IVlad: Số chữ số. Tôi đã làm rõ câu trả lời, cảm ơn bạn.
JoshD

Đối với thuật toán của OP, sử dụng các phép chia (số nguyên lớn) (chứ không phải các phân số) thì trên thực tế nó giống như O (n ^ 2 log ^ 2n).
Alexandre C.

@Alexandre C.: Hãy ghi nhớ n = ln b. Độ phức tạp thường xuyên của modulus đối với số nguyên lớn là gì? Có phải là O không (log n log ^ 2 log n)
JoshD

@JoshD: nó là một cái gì đó như vậy, tôi nghĩ rằng tôi đã bỏ lỡ một thuật ngữ log n, độ phức tạp cuối cùng (cho thuật toán với các phép chia) là O (n ^ 2 log ^ 2 n log n) trong trường hợp này.
Alexandre C.

13

Xem tại đây .

Cụ thể là phần này:

Lamé đã chỉ ra rằng số bước cần thiết để đi đến ước số chung lớn nhất cho hai số nhỏ hơn n là

văn bản thay thế

Giới O(log min(a, b))hạn trên tốt cũng vậy .


3
Điều đó đúng với số bước, nhưng nó không tính đến độ phức tạp của bản thân mỗi bước, tỷ lệ này được tính theo số chữ số (ln n).
JoshD

9

Đây là sự hiểu biết trực quan về độ phức tạp thời gian chạy của thuật toán Euclid. Các chứng minh chính thức được đề cập trong các văn bản khác nhau như Giới thiệu về các thuật toán và TAOCP Vol 2.

Đầu tiên, hãy nghĩ xem điều gì sẽ xảy ra nếu chúng ta cố gắng lấy gcd của hai số Fibonacci F (k + 1) và F (k). Bạn có thể nhanh chóng nhận ra rằng thuật toán Euclid lặp lại thành F (k) và F (k-1). Tức là, với mỗi lần lặp lại, chúng ta di chuyển xuống một số trong chuỗi Fibonacci. Vì các số Fibonacci là O (Phi ^ k) trong đó Phi là tỷ lệ vàng, chúng ta có thể thấy rằng thời gian chạy của GCD là O (log n) trong đó n = max (a, b) và log có cơ sở là Phi. Tiếp theo, chúng ta có thể chứng minh rằng đây sẽ là trường hợp xấu nhất bằng cách quan sát rằng các số Fibonacci luôn tạo ra các cặp trong đó phần còn lại đủ lớn trong mỗi lần lặp và không bao giờ trở thành số 0 cho đến khi bạn đến đầu chuỗi.

Chúng ta có thể làm cho O (log n) trong đó n = max (a, b) bị ràng buộc chặt chẽ hơn nữa. Giả sử rằng b> = a để chúng ta có thể viết bị ràng buộc tại O (log b). Đầu tiên, hãy quan sát rằng GCD (ka, kb) = GCD (a, b). Vì giá trị lớn nhất của k là gcd (a, c), chúng tôi có thể thay thế b bằng b / gcd (a, b) trong thời gian chạy của chúng tôi dẫn đến ràng buộc chặt chẽ hơn của O (log b / gcd (a, b)).


Bạn có thể đưa ra một bằng chứng chính thức rằng Fibonacci nos tạo ra trường hợp xấu nhất cho Euclids algo không?
Akash

4

Trường hợp xấu nhất của Thuật toán Euclid là khi phần còn lại là lớn nhất có thể ở mỗi bước, tức là. cho hai số hạng liên tiếp của dãy Fibonacci.

Khi n và m là số chữ số của a và b, giả sử n> = m, thuật toán sử dụng phép chia O (m).

Lưu ý rằng độ phức tạp luôn được đưa ra về kích thước của đầu vào, trong trường hợp này là số chữ số.


4

Trường hợp xấu nhất sẽ phát sinh khi cả n và m đều là các số Fibonacci liên tiếp.

gcd (Fn, Fn − 1) = gcd (Fn − 1, Fn − 2) = ⋯ = gcd (F1, F0) = 1 và số Fibonacci thứ n là 1.618 ^ n, trong đó 1.618 là tỷ lệ vàng.

Vì vậy, để tìm gcd (n, m), số lần gọi đệ quy sẽ là Θ (logn).


3

Đây là phân tích trong cuốn sách Cấu trúc dữ liệu và phân tích thuật toán trong C của Mark Allen Weiss (ấn bản thứ hai, 2.4.4):

Thuật toán của Euclid hoạt động bằng cách liên tục tính toán các phần còn lại cho đến khi đạt đến 0. Phần dư khác không cuối cùng là câu trả lời.

Đây là mã:

unsigned int Gcd(unsigned int M, unsigned int N)
{

    unsigned int Rem;
    while (N > 0) {
        Rem = M % N;
        M = N;
        N = Rem;
    }
    Return M;
}

Đây là một LÝ THUYẾT mà chúng tôi sẽ sử dụng:

Nếu M> N thì M mod N <M / 2.

BẰNG CHỨNG:

Có hai trường hợp. Nếu N <= M / 2, vì phần dư nhỏ hơn N nên định lý đúng cho trường hợp này. Trường hợp còn lại là N> M / 2. Nhưng sau đó N đi vào M một lần với phần dư M - N <M / 2, chứng minh định lý.

Vì vậy, chúng ta có thể đưa ra suy luận sau:

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

Vì vậy, sau hai lần lặp, phần còn lại nhiều nhất là một nửa giá trị ban đầu của nó. Điều này cho thấy rằng số lần lặp là nhiều nhất 2logN = O(logN).

Lưu ý rằng, thuật toán tính Gcd (M, N), giả sử M> = N. (Nếu N> M, lần lặp đầu tiên của vòng lặp sẽ hoán đổi chúng.)


2

Định lý Gabriel Lame giới hạn số bước bằng log (1 / sqrt (5) * (a + 1/2)) - 2, trong đó cơ sở của nhật ký là (1 + sqrt (5)) / 2. Đây là trường hợp xấu nhất cho thuật toán và nó xảy ra khi đầu vào là các số Fibanocci liên tiếp.

Một ràng buộc tự do hơn một chút là: log a, trong đó cơ sở của log là (sqrt (2)) được hàm ý bởi Koblitz.

Đối với mục đích mật mã, chúng tôi thường xem xét độ phức tạp theo từng bit của các thuật toán, có tính đến kích thước bit được cho xấp xỉ bằng k = loga.

Dưới đây là phân tích chi tiết về độ phức tạp theo bit của Thuật toán Euclid:

Mặc dù trong hầu hết các tài liệu tham khảo, độ phức tạp theo bit của Thuật toán Euclid được đưa ra bởi O (loga) ^ 3, nhưng tồn tại một giới hạn chặt chẽ hơn là O (loga) ^ 2.

Xem xét; 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

quan sát thấy: a = r0> = b = r1> r2> r3 ...> rm-1> rm> 0 .......... (1)

và rm là ước chung lớn nhất của a và b.

Bằng một tuyên bố trong cuốn sách của Koblitz (Một khóa học về Lý thuyết số và Mật mã) có thể được chứng minh rằng: ri + 1 <(ri-1) / 2 ................. ( 2)

Một lần nữa trong Koblitz, số phép toán bit cần thiết để chia một số nguyên dương k-bit cho một số nguyên dương l-bit (giả sử k> = l) được cho là: (k-l + 1) .l ...... ............. (3)

Theo (1) và (2) số ước là O (loga) và do (3) tổng độ phức tạp là O (loga) ^ 3.

Bây giờ điều này có thể được giảm xuống O (loga) ^ 2 bởi một nhận xét trong Koblitz.

coi ki = logri +1

bởi (1) và (2) ta có: ki + 1 <= ki cho i = 0,1, ..., m-2, m-1 và ki + 2 <= (ki) -1 cho i = 0 , 1, ..., m-2

và bởi (3) tổng chi phí của m ước số được giới hạn bởi: SUM [(ki-1) - ((ki) -1))] * ki cho i = 0,1,2, .., m

sắp xếp lại cái này: SUM [(ki-1) - ((ki) -1))] * ki <= 4 * k0 ^ 2

Vì vậy, độ phức tạp theo từng bit của Thuật toán Euclid là O (loga) ^ 2.


1

Tuy nhiên, đối với thuật toán lặp lại, chúng ta có:

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

Với các cặp Fibonacci, không có sự khác biệt giữa iterativeEGCD()iterativeEGCDForWorstCase()nơi mà cặp sau trông giống như sau:

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

Có, với Fibonacci Pairs, n = a % nn = a - nnó hoàn toàn giống như vậy.

Chúng tôi cũng biết rằng, trong một phản ứng trước cho cùng một câu hỏi, có một yếu tố giảm hiện hành: factor = m / (n % m).

Do đó, để định hình phiên bản lặp đi lặp lại của Euclidean GCD ở một dạng xác định, chúng ta có thể mô tả như một "trình mô phỏng" như sau:

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

Dựa trên công trình (slide cuối cùng) của Tiến sĩ Jauhar Ali, vòng lặp trên là logarit.

nhập mô tả hình ảnh ở đây

Vâng, nhỏ Oh vì trình mô phỏng cho biết số lần lặp lại nhiều nhất . Các cặp không phải Fibonacci sẽ có số lần lặp ít hơn Fibonacci, khi được khảo sát trên Euclidean GCD.


Vì nghiên cứu này được thực hiện bằng ngôn ngữ C, các vấn đề về độ chính xác có thể mang lại giá trị sai / không chính xác. Đó là lý do tại sao long long được sử dụng, để phù hợp hơn với biến dấu chấm động có tên là thừa số . Trình biên dịch được sử dụng là MinGW 2,95.
Mohamed Ennahdi El Idrissi

1

Ở mỗi bước, có hai trường hợp

b> = a / 2, thì a, b = b, a% b sẽ làm cho b nhiều nhất là một nửa giá trị trước đó của nó

b <a / 2, thì a, b = b, a% b sẽ tạo ra a nhiều nhất một nửa giá trị trước đó, vì b nhỏ hơn a / 2

Vì vậy, ở mỗi bước, thuật toán sẽ giảm ít nhất một số xuống ít nhất một nửa.

Trong nhiều nhất là bước O (log a) + O (log b) , điều này sẽ được giảm xuống các trường hợp đơn giản. Điều này mang lại một thuật toán O (log n), với n là giới hạn trên của a và b.

Tôi đã tìm thấy nó ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.