Наибольшим общим делителем (англ. greatest common divisor) целых неотрицательных чисел
Когда оба числа равны нулю, результат не определён — подойдёт сколько угодно большое число. Однако в этом случае мы положим в этом случае
Алгоритм Евклида находит
Алгоритм основывается на следующей несложной формуле:
Здесь предполагается, что
Докажем корректность этой формулы:
-
Если
$g = \gcd(a, b)$ делит и$a$ , и$b$ , то их разность$(a-b)$ тоже будет делиться на$g$ . -
Никакой больший делитель
$d$ числа$b$ не может делить число$(a-b)$ : если$d > g$ , то$d$ не может делить$a$ , а значит и не делит$(a - b)$ .
Прямая рекурсивная реализация:
int gcd(int a, int b) {
if (a < b)
swap(a, b);
if (b == 0)
return a;
else
return gcd(b, a - b);
}
Этот алгоритм может работать долго — например, на паре
Простой способ этого достичь — просто вычесть
Можно показать, что каждые две итерации меньшее число уменьшится хотя бы в два раза, а следовательно алгоритм работает за
Реализация:
int gcd(int a, int b) {
if (b == 0)
return a;
else
return gcd(b, a % b);
}
Чуть более быстрая итеративная форма:
int gcd(int a, int b) {
wihle (b > 0) {
a %= b;
swap(a, b);
}
return a;
}
В компиляторе gcc
для этого уже есть встроенная функция __gcd
, которая, впрочем, может непредсказуемо себя вести на отрицательных числах и
Оценка
Примечательно, что худшие входные данные для алгоритма — это соседние числа Фибоначчи. На графике они видны как синие точки в пропорциях золотого сечения.
Также иногда полезно знать, что нахождение
Для обычного использования
Расширенный алгоритм Евклида находит, помимо
Эта модификация алгоритма интересна, потому что с помощью неё можно искать обратный элемент по модулю: такой элемент
Заметим также, что решений бесконечно много: можно
Алгоритм будет тоже рекурсивный. Пусть мы посчитали нужные коэффициенты
Чтобы получить решение для исходной пары, запишем выражение
Теперь выполним перегруппировку слагаемых (сгруппируем по исходным
Сравнивая это с исходным выражением, получаем, что для иходных
Реализация:
int gcd(int a, int b, int &x, int &y) {
if (a == 0) {
x = 0;
y = 1;
return b;
}
int x1, y1;
int d = gcd(b % a, a, x1, y1);
x = y1 - (b / a) * x1;
y = x1;
return d;
}
Эта рекурсивная функция по прежнему возвращает значение