Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
89
Добавлен:
01.05.2014
Размер:
484.86 Кб
Скачать

1.7. Бинарный алгоритм нахождения нод

Напомним, что алгоритм Евклида, рассмотренный ранее, основывался на следующих свойствах НОД:

  1. (a > b > 0)  gcd(a, b) = gcd(b, a mod b);

  2. gcd(a, 0) = a.

Медленная, но верная версия алгоритма Евклида, использующая операцию вычитания, основывается на других свойствах НОД, а именно:

  1. (a > b > 0)  gcd(ab) = gcd(a  bb);

  2. gcd(ab) = gcd(ba);

  3. gcd(aa) = a.

Рассмотрим ещё одну версию алгоритма Евклида, использующую в качестве базовых операций деление на 2 и вычитание. Эта версия называется бинарным алгоритмом. Несмотря на то, что число шагов в этом алгоритме больше, чем в классическом алгоритме Евклида, использующем операцию деления, быстродействие данного алгоритма может оказаться выше, так как его базовая операция «div 2» выполняется компьютером быстрее. Бинарный алгоритм нахождения НОД основывается на следующих свойствах (здесь использовано обозначение Even(x) = not Odd(x)):

  1. Even(a) & Even(b)  gcd(ab) = 2 gcd(a div 2, b div 2);

  2. Odd(a) & Even(b)  gcd(ab) = gcd(ab div 2);

  3. gcd(ab) = gcd(ba);

  4. (a > b)  gcd(ab) = gcd(a  bb);

  5. Odd(a) & Odd(b)  Even(a  b) & (|a  b| < max(ab) ).

Cвойства 3 и 4 уже использовались, свойство 5 очевидно, а свойства 1 и 2 можно обосновать, рассматривая разложение (1.1) чисел a и b на простые множители (интерес представляют только степени 2) и учитывая (1.2).

Общая идея бинарного алгоритма такая же, как и в алгоритме Евклида: пользуясь свойствами 1–5, переходить от пары чисел a и b к новой паре a* и b*, таких, что max(a*b*) < max(ab) и, кроме того, gcd(a*b*) = gcd(ab), либо gcd(a*b*)  связан известным образом с gcd(ab). Разделим действия алгоритма на два этапа. На первом этапе определим по свойству 1 число k (степень 2 в gcd(ab)) и такие u и v, что gcd(ab) = k gcd(uv). Затем на втором этапе найдём такие u* и v*, что gcd(u*v*) = gcd(uv) и либо u* = 0, либо v* = 0. Тогда отличное от 0 число u или v и будет искомым НОД.

Первый этап реализуется следующим образом:

u := a; v := b; k := 1;

{ u>0 & v>0 & gcd(ab) = k*gcd(uv) }

while not Odd(u) and not Odd(v) do

begin u := u div 2; v := v div 2; k := k*2

end;

{ u>0 & v>0 & gcd(ab) = k*gcd(uv) & (Odd(u) or Odd(v)) }

Перед вторым этапом хотя бы одно из чисел u или v будет нечётным. Тогда для второго этапа запишем набросок цикла (k здесь уже не изменится):

{ u>0 & v>0 & gcd(ab) = k*gcd(uv) & (Odd(u) or Odd(v)) }

repeat

ТЕЛО ЦИКЛА

{ gcd(ab) = k*gcd(uv) }

until (u=0) xor (v=0);

{ gcd(ab) = k*gcd(uv) & (Odd(u) xor Odd(v)) }

if v=0 then gcd := u*k else gcd := v*k; {gcd = gcd(ab)}

Тело этого цикла можно реализовать следующим образом. Сначала сделаем так, чтобы оба числа u и v стали нечётными (если это не так при входе в тело цикла):

{ (u>0 & v>0) & (Odd(u) or Odd(v)) }

while not Odd(u) do u := u div 2;

while not Odd(v) do v := v div 2;

{ Odd(u) & Odd(v)}

Затем, используя свойства 35, добьёмся уменьшения max(uv) так, чтобы одно из u или v стало чётным:

{ (u>0 & v>0) & Odd(u) & Odd(v)}

if u>v then u := uv else v := vu; { Уменьшение max(uv)}

{Odd(u) xor Odd(v) }

Заметим, что при такой реализации сравнения u и v в условном операторе if-then-else только значение v может стать равным 0. Поэтому можно упростить условие окончания цикла repeat-until, заменив его на условие v = 0. Кроме того, по завершении цикла теперь надо установить gcd(ab) = u*k без выбора. В итоге бинарный алгоритм принимает следующий вид:

function GCD1(a, b: Nat0): Nat0;

{ Бинарный структурированный вариант алгоритма Евклида }

var u,v,k: Nat0;

begin

u := a; v := b; k := 1;

{ u>0 & v>0 & gcd(uv) = gcd(ab) }

while not Odd(u) and not Odd(v) do

begin u := u div 2; v := v div 2; k := k*2

end;

{ (u>0 & v>0) & gcd(ab) = k*gcd(uv) & (Odd(u) or Odd(v)) }

repeat { на 1-м шаге  Odd(u) or Odd(v), далее  Odd(u) xor Odd(v) }

while not Odd(u) do u := u div 2;

while not Odd(v) do v := v div 2;

{ Odd(u) & Odd(v)}

if u>v then u := uv else v := vu; { Уменьшение max(u,v)}

{Odd(u) xor Odd(v) }

until (v=0);

{ gcd(ab) = k*gcd(uv) & (Odd(u) xor Odd(v)) & v=0 }

GCD1 := u*k;

end {GCD1}

Приведём примеры работы бинарного алгоритма и для сравнения – алгоритма Евклида.

Пример 1: a = 900, b = 280. Результаты работы бинарного алгоритма приведены в табл. 1.2, а результаты работы алгоритма Евклида – в табл. 1.3. В каждой строке таблиц даны значения переменных после завершения очередного шага. В табл. 1.2 изменённые значения переменных даны жирным

Таблица 1.2

Номер шага (сквозной)

Номер этапа

Номер шага цикла

Номер шага в теле цикла

Значение u

Значение v

Значение k

0

1

0

-

900

280

1

1

1

1

-

450

140

2

2

1

2

-

225

70

4

3

2

1

1-while

225

35

4

4

2

1

2-if

190

35

4

5

2

2

1-while

95

35

4

6

2

2

2-if

60

35

4

7

2

3

1-while

30

35

4

8

2

3

2-while

15

35

4

9

2

3

3-if

15

20

4

10

2

4

1-while

15

10

4

11

2

4

2-while

15

5

4

12

2

4

3-if

10

5

4

13

2

5

1-while

5

5

4

14

2

5

2-if

5

0

4

Результат: gcd(ab) = u*k = 5*4 = 20

Таблица 1.3

Номер шага

Значение u

Значение v

0

900

280

1

280

60

2

60

40

3

40

20

4

20

0

Результат: gcd(ab) = u = 20

курсивом. Бинарный алгоритм завершился за 14 шагов, а алгоритм Евклида – за 4. Отметим, что шагом считается: а) в алгоритме Евклида  операция нахождения остатка mod; б) в бинарном алгоритме  на первом этапе итерация цикла, содержащая два деления на 2 и одно умножение на 2, а на втором этапе операция деления на 2 (в цикле while-do) или вычитания (в условном операторе if-then-else).

Таблица 1.4

Номер шага (сквозной)

Номер этапа

Номер шага цикла

Номер шага в теле цикла

Значение u

Значение v

Значение k

0

1

0

-

610

377

1

1

2

1

1-while

305

377

1

2

2

1

2-if

305

72

1

3

2

2

1-while

305

36

1

4

2

2

2-while

305

18

1

5

2

2

3-while

305

9

1

6

2

2

4-if

296

9

1

7

2

3

1-while

148

9

1

8

2

3

2-while

74

9

1

9

2

3

3-while

37

9

1

10

2

3

4-if

28

9

1

11

2

4

1-while

14

9

1

12

2

4

2-while

7

9

1

13

2

4

3-if

7

2

1

14

2

5

1-while

7

1

1

15

2

5

2-if

6

1

1

16

2

6

1-while

3

1

1

17

2

6

2-if

2

1

1

18

2

7

1-while

1

1

1

19

2

-

2-if

1

0

1

Результат: gcd(ab) = u*k = 1*1 = 1

Пример 2: a = 610, b = 377. Исходными данными являются два соседних числа Фибоначчи, и алгоритм Евклида затратит максимальное (в данном диапазоне) количество шагов (объяснение этого факта дано в 1.5). Для би-нарного алгоритма результаты приведены в табл. 1.4, а для алгоритма Евкли-

Таблица 1.5

Номер шага

Значение u

Значение v

Номер шага

Значение u

Значение v

0

610

377

8

13

8

1

377

233

9

8

5

2

233

144

10

5

3

3

144

89

11

3

2

4

89

55

12

2

1

5

55

34

13

1

0

6

34

21

Результат: gcd(ab) = u = 1

7

21

13

да – в табл. 1.5. Бинарный алгоритм завершился за 19 шагов, а алгоритм Евклида – за 13.

Перед тем как провести эмпирическое (экспериментальное) сравнение времен выполнения двух рассмотренных алгоритмов, познакомимся ещё с одной версией бинарного алгоритма.