§ Теоретическое обоснование
Сегодня я попытаюсь разобрать алгоритм Евклида в его расширенном варианте. Что это значит? Что значит "расширенный"? Это просто. Вот мы нашли НОД(a,b)=d, но нам надо узнать вот что:То есть, нам надо найти такой x и такой y, при умножении на которое у нас получится d (см. формулу выше). Вот пример. Найдем НОД(3,12) и он равен 3, потому что как 12, так и 3 делятся на 3. Хорошо, мы его нашли, выпишем:
Где a=3, b=12, d=НОД(3,12). И как теперь узнать, какие будут x, y? Автор классной книги, его звать Кнут (и пряник, ага), нашел этот отличный метод и назвал его "Расширенный алгоритм Евклида". Начнем по-порядку. Сначала надо вспомнить то выражение, которое мы разбирали в прошлой статье про алгоритм Евклида:
Где НОД(a,b) = НОД(b,r). Это было доказано ранее. Теперь посмотрим, как происходит обычно поиск НОД по этому алгоритму. Как мы помним, чтобы перейти к следущей итерации, надо разделить "a" на "b", получить q, и остаток, и применить их как новые "a" и "b".
Шаг | Поиск НОД | Разложение остатков | Описание |
---|---|---|---|
1 | , теперь они вместо | ||
2 | , теперь они вместо | ||
3 | , теперь они вместо | ||
4 | , теперь они вместо | ||
.... | |||
, теперь они вместо | |||
Точка остановки |
И еще то, что:
И поэтому:
Давайте теперь подставим в формулу выше формулу ту, что ниже:
Раскроем скобки и перегруппируем:
Теперь приглядимся внимательно... так вот что мы видим? Мы видим, что коэффициентами перед a и b у нас стоят
и :
Да это просто шикарно! То есть, чтобы вычислить следующий и , нужно всего лишь знать предыдущие 2! Вот в этом и есть сила расширенного алгоритма Евклида.
Тут встает вопрос. Вот мы хотим вычислить и , то есть, нам нужны коэффициенты (, , ), и где их достать? А тут все просто. Взглянем на таблицу. У нас , то если мы попробуем найти , то в таком случае , и , судя по таблице.
Это значит, что:
Отсюда:
Вот такой вот он, расширенный алгоритм Евклида. И результатом НОД будет .
§ Программная реализация
А теперь время программного обеспечения.// Расширенный НОД int extnod(int a, int b, int* x, int* y) { int q, r; int x2 = 1, y2 = 0, // x(i-2)=1, y(i-2)=0 x1 = 0, y1 = 1; // x(i-1)=0, y(i-1)=1 do { // Алгоритм Евклида q = a / b; r = a % b; a = b; b = r; // Расширенный алгоритм Евклида *x = x2 - x1*q; // x(i) = x(i-2) - x(i-1)*q *y = y2 - y1*q; // y(i) = y(i-2) - y(i-1)*q // "Сдвиг истории" назад x2 = x1; y2 = y1; // Старый x(i-1), y(i-1) перемещается в x(i-2), y(i-2) x1 = *x; y1 = *y; // Новый x(i), y(i) становится x(i-1), y(i-1) } while (r > 0); // Перебирать, пока не станет r = 0 // И записать разультат, где x = x(i-1), y = y(i-1) // т.к. ранее мы "сдвинули" в (x2, y2) старые (x1, y1) *x = x2; *y = y2; // Вернуть значение НОД return a; }