Складність у часі алгоритму Евкліда


97

Мені важко вирішити, якою є часова складність найбільшого загального знаменника алгоритму Евкліда. Цей алгоритм у псевдокоді:

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

Здається, це залежить від a та b . Я думаю, що складність часу становить O (a% b). Це правильно? Чи є кращий спосіб написати це?


14
Див. Knuth TAOCP, том 2 - він дає широке висвітлення. Просто FWIW, пара лакоміць: це не пропорційно a%b. Найгірший випадок - це коли aі bє послідовними числами Фібоначчі.
Джеррі Коффін,

3
@JerryCoffin Примітка. Якщо ви хочете довести, що найгірший випадок справді є числами Фібоначчі більш офіційним способом, подумайте про те, щоб довести, що n-й крок перед закінченням повинен бути принаймні таким великим, як gcd, помножений на n-те число Фібоначчі з математичною індукцією.
Mygod

Відповіді:


73

Одним фокусом для аналізу часової складності алгоритму Евкліда є дотримання того, що відбувається за дві ітерації:

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

Тепер значення a та b зменшуватимуться замість лише одного, що полегшує аналіз. Ви можете розділити його на випадки:

  • Крихітний A: 2a <= b
  • Крихітний 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%
  • Крихітний B: a % b < bі 2b <= 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. Загальна кількість кроків ( S), поки ми не досягнемо 0, повинна відповідати (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)

Отже, кількість ітерацій є лінійною за кількістю вхідних цифр. Для чисел, які входять в регістри процесора, доцільно моделювати ітерації як такі, що займають постійний час, і робити вигляд, що загальний час роботи 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.
Крейг Гідні,

@ Cheersandhth.-Alf Ви вважаєте незначну різницю у бажаній термінології "серйозно неправильною"? Звичайно, я використовував термінологію CS; це питання інформатики. Незалежно від того, я пояснив відповідь, сказавши "кількість цифр".
Крейг Гідні

@CraigGidney: Дякую, що це виправили. Зараз я впізнаю проблему спілкування з багатьох статей Вікіпедії, написаних чистими науковцями. Розгляньте це: основна причина говорити про кількість цифр, а не просто писати O (log (min (a, b))), як я це робив у своєму коментарі, полягає в тому, щоб зробити речі простішими для розуміння для нематематичних людей. Без цього просто напишіть "журнал" і т. д. Отже, це мета кількості цифр, щоб допомогти тим, кому кидають виклик. Коли ви називаєте це поняття "розмір" і маєте визначення в іншому місці, і не говоріть про "журнал" у Зрештою, ви затуманюєте замість допомоги.
Вітаю та щось! - Альф

Останній абзац неправильний. Якщо підсумувати відповідні телескопічні серії, ви виявите, що часова складність складає просто O (n ^ 2), навіть якщо ви використовуєте алгоритм квадратичного поділу шкільної книги.
Еміль Йержабек,

27

Підходящим способом аналізу алгоритму є визначення його найгірших сценаріїв. Найгірший випадок евклідового GCD відбувається, коли беруть участь пари Фібоначчі. void EGCD(fib[i], fib[i - 1]), де i> 0.

Наприклад, давайте зупинимося на випадку, коли дивіденд дорівнює 55, а дільник - 34 (нагадаємо, що ми все ще маємо справу з числами Фібоначчі).

введіть тут опис зображення

Як ви могли помітити, ця операція коштувала 8 ітерацій (або рекурсивних викликів).

Давайте спробуємо більші числа Фібоначчі, а саме 121393 та 75025. Тут ми також можемо помітити, що це зайняло 24 ітерації (або рекурсивні виклики).

введіть тут опис зображення

Ви також можете помітити, що кожна ітерація дає число Фібоначчі. Тому у нас так багато операцій. Ми не можемо отримати подібних результатів лише з числами Фібоначчі.

Отже, складність часу буде представлена ​​малим О (верхня межа), цього разу. Нижня межа - інтуїтивно Омега (1): наприклад, випадок 500 ділиться на 2.

Вирішимо відношення рецидиву:

введіть тут опис зображення

Тоді ми можемо сказати, що Евклідова GCD може виконувати журнал (xy) як максимум .


2
Я думаю, що цей аналіз помилковий, оскільки база залежить від вхідних даних.
Сподіваємось,

Чи можете ви довести, що залежна база представляє проблему?
Мохамед Еннаді Ель Ідріссі,

1
Основою, очевидно, є золотий перетин. Чому? Оскільки потрібен рівно один додатковий крок, щоб обчислити кивок (13,8) проти кивання (8,5). Для фіксованого x, якщо y <x, найгіршим показником є ​​x = fib (n + 1), y = fib (n). Тут y залежить від x, тому ми можемо дивитись лише на x.
Степан

17

Це чудовий погляд на статтю Вікіпедії .

Він навіть має приємний сюжет складності для пар цінностей.

Це не так O(a%b).

Відомо (див. Статтю), що воно ніколи не займе більше кроків, ніж п’ятикратна кількість цифр у меншій кількості. Тож максимальна кількість кроків зростає із збільшенням кількості цифр (ln b). Вартість кожного кроку також зростає із збільшенням кількості цифр, тому складність пов'язана з тим, O(ln^2 b)де b - менша кількість. Це верхня межа, а фактичний час, як правило, менше.


Що означає n?
IVlad

@IVlad: Кількість цифр. Я пояснив відповідь, дякую.
JoshD

Для алгоритму OP, використовуючи (великі цілі) ділення (а не віднімання), насправді це щось більше схоже на O (n ^ 2 log ^ 2n).
Олександр К.

@Alexandre C: Майте на увазі n = ln b. Яка регулярна складність модуля для великого int? Це O (log n log ^ 2 log n)
JoshD

@JoshD: це щось подібне, я думаю, що я пропустив log n термін, остаточна складність (для алгоритму з діленнями) - O (n ^ 2 log ^ 2 n log n) у цьому випадку.
Alexandre C.

13

Дивіться тут .

Зокрема ця частина:

Ламе показав, що кількість кроків, необхідних для досягнення найбільшого спільного дільника для двох чисел, менших за n, становить

alt текст

Так O(log min(a, b))само хороша верхня межа.


3
Це справедливо для кількості кроків, але це не враховує складність кожного кроку, який масштабується за кількістю цифр (ln n).
JoshD

9

Ось інтуїтивне розуміння складності виконання алгоритму Евкліда. Формальні докази висвітлюються в різних текстах, таких як Вступ до алгоритмів та TAOCP, том 2.

Спочатку подумайте, що, якби ми спробували взяти gcd з двох чисел Фібоначчі F (k + 1) і F (k). Ви можете швидко помітити, що алгоритм Евкліда повторюється до F (k) та F (k-1). Тобто, з кожною ітерацією ми рухаємося вниз на одне число в серії Фібоначчі. Оскільки числа Фібоначчі - O (Phi ^ k), де Phi - золотий перетин, ми можемо бачити, що час роботи GCD становив O (log n), де n = max (a, b), а log має базу Phi. Далі ми можемо довести, що це було б найгіршим випадком, спостерігаючи, що числа Фібоначчі послідовно створюють пари, де залишки залишаються досить великими на кожній ітерації і ніколи не стають нульовими, поки ви не прибудете на початок серії.

Ми можемо зробити O (log n), де n = max (a, b), зв’язані ще щільніше. Припустимо, що 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)).


Чи можете ви надати офіційне підтвердження того, що номери Фібоначчі є найгіршим для Альго Евкліда?
Akash

4

Найгірший випадок алгоритму Евкліда - це коли решта є найбільшою з можливих на кожному кроці, тобто. для двох послідовних членів послідовності Фібоначчі.

Коли 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

Ось аналіз в книзі структури даних і аналізу алгоритму в С допомогою Mark Allen Weiss (друге видання, 2.4.4):

Алгоритм Евкліда працює, постійно обчислюючи залишки до досягнення 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 mod N <M / 2.

ДОКАЗ:

Є два випадки. Якщо N <= M / 2, то, оскільки залишок менший за N, теорема справедлива для цього випадку. Інший випадок - N> M / 2. Але тоді N один раз переходить у M із залишком M - N <M / 2, доводячи теорему.

Отже, ми можемо зробити такий висновок:

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

Зверніть увагу, що алгоритм обчислює Gcd (M, N), припускаючи M> = N. (Якщо N> M, перша ітерація циклу міняє місцями).


2

Теорема Габріеля Ламе обмежує кількість кроків за log (1 / sqrt (5) * (a + 1/2)) - 2, де основа журналу дорівнює (1 + sqrt (5)) / 2. Це для найгіршого сценарію для алгоритму, і це відбувається, коли вхідні дані є послідовними числами Фібаноччі.

Дещо ліберальніша межа: log a, де основа журналу (sqrt (2)), мається на увазі під Кобліцем.

Для криптографічних цілей ми зазвичай враховуємо побітову складність алгоритмів, беручи до уваги, що розрядність задається приблизно за k = loga.

Ось детальний аналіз побітової складності Евкліда Алгоріта:

Хоча в більшості посилань побітова складність Евклідового алгоритму задається 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.

Ствердженням у книзі Кобліца (Курс теорії чисел та криптографії) можна довести, що: ri + 1 <(ri-1) / 2 ................. ( 2)

Знову в Кобліці кількість бітових операцій, необхідних для розділення k-бітового додатного цілого числа на l-бітове додатне ціле число (припускаючи k> = l), дається як: (k-l + 1) .l ...... ............. (3)

За (1) та (2) кількість розділів дорівнює O (loga), а отже, за (3) загальна складність дорівнює O (loga) ^ 3.

Тепер це може бути зведено до O (loga) ^ 2 зауваженням у Кобліці.

розглянемо ki = logri +1

за (1) та (2) маємо: ki + 1 <= ki для i = 0,1, ..., m-2, m-1 та ki + 2 <= (ki) -1 для i = 0 , 1, ..., м-2

і (3) загальна вартість m дивізіонів обмежена: SUM [(ki-1) - ((ki) -1))] * ki для i = 0,1,2, .., m

переставляючи це: SUM [(ki-1) - ((ki) -1))] * ki <= 4 * k0 ^ 2

Отже, побітова складність алгоритму Евкліда дорівнює 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 % 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);
}

На основі роботи (останній слайд) доктора Джаухара Алі, цикл вище є логарифмічним.

введіть тут опис зображення

Так, маленький О, тому що симулятор повідомляє кількість ітерацій максимум . Непари Фібоначчі потребують меншої кількості ітерацій, ніж Фібоначчі, коли їх досліджують на Евклідовому GCD.


Оскільки це дослідження проводилось із використанням мови С, проблеми з точністю можуть давати помилкові / неточні значення. Ось чому використовується long long , щоб краще підходити до змінної з плаваючою комою, з назвою коефіцієнт . Використовуваний компілятор - MinGW 2,95.
Мохамед Еннаді Ель Ідріссі 01.03.14

1

На кожному кроці є два випадки

b> = a / 2, тоді a, b = b, a% b складе b щонайбільше половину свого попереднього значення

b <a / 2, тоді a, b = b, a% b складе максимум половину свого попереднього значення, оскільки b менше, ніж a / 2

Отже, на кожному кроці алгоритм зменшить хоча б одне число принаймні наполовину менше.

Щонайбільше на етапі O (log a) + O (log b) це буде зведено до простих випадків. Які дають алгоритм O (log n), де n - верхня межа a та b.

Я знайшов його тут

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.