Алгоритм Евкліда знаходження НСД
Дано два цілих невід'ємних числа і . Потрібно знайти їх найбільший спільний дільник, тобто найбільше таке число, яке є дільником одночасно і , і . На англійській мові "найбільший спільний дільник" пишеться "greatest common divisor", і позначається як :
(тут символом "" позначено ділимість, тобто "" означає " націло ділить ")
Коли одне з чисел рівне нулю, а інше відмінне від нуля, тоді їх найбільшим спільним дільником, згідно визначенню, буде друге число. Коли обидва числа рівні нулю, результат не визначений (підійде будь-яке нескінченно велике число), тому покладемо у цьому випадку найбільший спільний дільник рівним нулю. Тому можна говорити про таке правило: якщо одне з чисел рівне нулю, то їх найбільший спільним дільник рівний другому числу.
Алгоритм Евкліда, що розглядається нижче, розв'язує задачу знаходження найбільшого спільного дільника двох чисел і за .
Даний алгоритм був вперше описаний у книзі Евкліда "Начала" (близько 300 р. до н.е).
Алгоритм
Сам алгоритм надзвичайно простий і описується наступною формулою:
Реалізація
int gcd(int a, int b) {
if (b == 0) {
return a;
}
else {
return gcd(b, a % b);
}
}
Використовуючи тернарний умовний оператор C++, алгоритм можна записати ще коротше:
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
Оптимальніша нерекурсивна форма алгоритму:
int gcd(int a, int b) {
while (b) {
a %= b;
swap(a, b);
}
return a;
}
Доведення коректності
Спочатку зауважимо, що при кожній ітерації алгоритму Евкліда його другий аргумент завжди зменшується, а отже, оскільки він невід'ємний, алгоритм Евкліда завжди завершується.
Для доведення коректності нам необхідно показати, що для будь-яких .
Покажемо, що величина, що стоїть в лівій частини рівності, ділиться на величину у правій, а та, шо стоїть у правій - ділиться на величину у лівій. Очевидно, це буде означати, що ліва і права частини збігаються, що і доведе коректність алгоритму Евкліда.
Позначимо . Значить, за визначенням, і .
Далі, розкладемо залишок від ділення на через їх частку:
Звідси випливає:
Отже, згадуючи твердження , отримуємо систему:
Скористаємося тепер наступним простим фактом: якщо для якихось трьох чисел виконуються: і , то виконується і: . У нашій ситуації отримуємо:
Або, підставляючи замість його визначення як , отримуємо:
Отже, ми здійснили половину доведення: показали, що ліва частина ділить праву. Друга половина доведення аналогічна.
Час роботи
Час роботи алгоритму оцінюється теоремою Ламе, яка встановлює дивовижний зв'язок алгоритму Евкліда і послідовності Фібоначчі:
Якщо і для деякого , то алгоритм Евкліда виконає не більше рекурсивних викликів.
Більше того, можна показати, що верхня межа цієї теореми - оптимальна. При буде виконано саме рекурсивних викликів. Іншими словами, послідовні числа Фібоначчі - найгірші вхідні дані для алгоритму Евкліда.
Враховуючи, що числа Фібоначчі ростуть експоненціально (як константа в степені ), отримуємо, що алгоритм Евкліда виконується за операцій множення.
Застосування
НСК
Обчислення найменшого спільного кратного (least common multiplier, lcm) зводиться до обчислення наступним простим твердженням:
Таким чином, обчислення НСК також можна здійснити за допомогою алгоритму Евкліда, з тією ж асимптотикою:
int lcm(int a, int b) {
return a / gcd(a, b) * b;
}
Варто спочатку поділити на , а тільки потім помножити на , оскільки це допоможе уникнути переповнення типу у деяких випадках.