Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Расширенный алгоритм Евклида.doc
Скачиваний:
2
Добавлен:
25.09.2019
Размер:
128.51 Кб
Скачать

Расширенный алгоритм Евклида

Пусть есть два отрезка. Их длины a и b – целые числа (далее, говоря о числах, считаем их всегда целыми). Скажем, a=2322, а b=654.

Если, например, отложить на прямой отрезок a, затем, в обратном направлении, три раза b, то получим отрезок длиной 360 (рис. 1.21). Действительно, a+b·(3)=2322+654·(3)=360.

Рис. 1.21. Пример откладывания на прямой отрезков длины a и b

Можно получить и отрезок меньшей длины. Действуем так: a·2+b·(7)= 2322·2+654·(–7)=66 – два раза откладываем отрезок a и семь раз отрезок b в обратном направлении. Возникает, как минимум, четыре вопроса.

1. Какую наименьшую длину удастся получить, «орудуя» заданными отрезками a и b?

2. Как это сделать, сколько раз взять отрезок a и сколько раз отрезок b?

3. Какой длины отрезки отмеряются с помощью a и b?

4. Если отрезок заданной длины отмеряется, то, как это сделать?

Первый вопрос

Итак, отрезок какой наименьшей длины можно получить, используя отрезки a и b?

Эксперимент. Пусть a и b – положительные, фиксированные, а x и y –произвольные целые числа. Напишем процедуру, которая перебирает числа вида a·x+b·y и находит среди них минимальное положительное.

Procedure Small (a,b:Integer; Var min:Integer);

Const k=500;

Var x,y,t:Integer;

Begin

min:=a;

For x:=-k To k Do

For y:=-k To k Do Begin

t:=a*x+b*y;

If (t>0)And(t<min) Then min:=t;

End;

End;

Перебор ограничен константой k и выглядит несколько искусственным, но он достаточен для генерации идеи (гипотезы). Экспериментируя, заполняем таблицу (табл. 1.6).

Примечание. Убедитесь, что данные в табл. 1.6 не изменятся и при больших k, а гипотеза будет верна и для других a и b.

Таблица 1.6

a

b

min

10

15

5

18

24

6

105

165

15

64

32

32

17

53

1

9

55

1

54

45

9

2322

654

6

Гипотеза. Обозначим множество всех чисел вида a·x+b·y буквой P. Возьмём minнаименьшее положительное число из P. Оказывается, это наибольший общий делитель a и b.

Второй вопрос

Как отмерить отрезок наименьшей длины? Сколько раз для этого взять отрезок a и сколько раз отрезок b?

Почти всегда срабатывает метод «грубой силы» – перебор. Эта задача не исключение. Отрезки a и b даны. Наименьшую длину d=НОД(a,b) находим по алгоритму Евклида. После этого подбираем целые x и y такие, чтобы a·x+b·y=d. Для этого ищем целое x такое, при котором и y=(d–a·x)/b также целое. Начинаем с x=0, а затем «движемся» от него по числовой прямой, в двух направлениях одновременно, с шагом 1.

Procedure Simple(a,b,d:Integer; Var x,y:Integer);

Var xn,xp:Integer;

Begin

xp:=0; xn:=0;

While ((d-a*xp) Mod b<>0) And ((d-a*xn) Mod b<>0) Do

Begin

xp:=xp+1;

xn:=xn-1;

End;

If (d-a*xp) Mod b=0 Then x:=xp Else x:=xn;

y:=(d-a*x)Div b;

End;

Цикл гарантированно завершится. Ведь искомое x точно есть, либо справа от нуля, либо слева.

Однако, узнать и сам НОД(a,b), и его представление в виде a·x+b·y, где x и y – целые числа, можно одновременно и без перебора. Для этого требуется изменить (расширить) стандартную версию алгоритма Евклида.

Расширенный итеративный алгоритм Евклида

Нам предстоит дополнить знакомую логику реализации алгоритма Евклида.

Procedure Euclid (a,b:Integer; Var d:Integer);

Var r:Integer;

Begin

While b>0 Do Begin

r:=a Mod b;

a:=b;

b:=r;

End;

d:=a;

End;

В этой логике, идя по цепочке остатков от деления, мы приближаемся к цели – находим наибольший общий делитель. Например, 2322, 654, 360, 294, 66, 30, 6, 0. Последний ненулевой остаток и есть наибольший общий делитель.

Задача заключается в том, чтобы выразить наибольший общий делитель через первые два числа в цепочке. Обозначим их и . Это в перспективе. А пока выразим эти первые два числа через себя самих же.

= ·1+ ·0, = ·0+ ·1.

Вопрос. Допустим, мы знаем для некоторых чисел a и b, как их представить с помощью и :

a= ·xa+ ·ya, b= ·xb+ ·yb.

Как найти такое же представление для остатка r от деления а на b?

Ответ. Имеем a=q∙b+r, 0≤r<b. Отсюда,

r=a-q∙b=( ·xa+ ·ya)-q∙( ·xb+ ·yb)= ·(xa-q∙xb)+ ·(ya-q∙yb),

или

r= ·xr+ ·yr,

где

xr=xa-q∙xb, yr=ya-q∙yb.

Итак, нам известно, с чего начать и как делать шаг по цепочке вперед. Вооружившись формулами, выражаем через и каждый новый остаток. Пока не доберёмся до наибольшего общего делителя.

Пример. Пусть a=2322, b=654. Результаты вычислений показаны в табл. 1.7. Первый столбик – это остатки от деления r. Второй – целые части q. Третий и чётвёртый – искомые множители x и y.

В первые две строки помещаем исходные числа и их тривиальные представления. Каждую следующую строку-представление строим по двум предыдущим. Из верхней вычитаем нижнюю, помноженную на q.

Таблица 1.7

r

q

x

y

2322

1

0

654

3

0

1

360

1

1

-3

294

1

-1

4

66

4

2

-7

30

2

-9

32

6

5

20

-71

Всего пять шагов – и мы готовы выписать ответ. НОД(2322, 654) = 6 = 2322·20+654·(-71). То есть. Во-первых, минимальная длина, которую можно отмерить отрезками a=2322 и b=654, равна 6. Во-вторых, для этого нужно отложить 20 раз a, и в обратном направлении 71 раз b.

Procedure ExEuclid (a,b:Integer; Var d,x,y:Integer);

Var r,q,xa,ya,xb,yb,xr,yr:Integer;

Begin

{На старте в цепочке два числа. Это их очевидные представления.}

xa:=1; ya:=0;

xb:=0; yb:=1;

While b>0 Do Begin

{Вычисляем очередной остаток.}

r:=a Mod b;

{А также его представление.}

q:=a Div b;

xr:=xa-q*xb;

yr:=ya-q*yb;

{Цепочка «вырастает» ещё на одно число. Смещаем a и b на её конец. Запоминать то, что было до, нет необходимости. Теперь в роли a будет b, в роли b будет r.}

a:=b;

xa:=xb; ya:=yb;

b:=r;

xb:=xr; yb:=yr;

End;

{После цикла в a и b хранятся последние числа полной цепочки. а – ненулевое. Оно-то нам и требуется, вместе со своим представлением.}

d:=a;

x:=xa; y:=ya;

End;