Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Костюк - Основы программирования

.pdf
Скачиваний:
134
Добавлен:
30.05.2015
Размер:
1.3 Mб
Скачать

Глава 3 Алгоритмы с одним основным циклом

3.1 Рекуррентные последовательности и алгоритмы

Определение 3.1. Числовая последовательность {xk} называется рекуррентной

ранга p, если

 

 

 

ì xk

= ak ,

k = 0, 1, ..., p -1,

(3.1)

í

= f (k, xk 1 , xk 2 ,..., xk p ),

k = p, p + 1, ...

îxk

 

где a0, a1, …, ap – 1 – константы, а f – функция.

Определение рекуррентной последовательности само является алгоритмом, кото­ рый надо просто "переписать" на Паскале. Этот алгоритм состоит из присваиваний начальных значений элементам x0, x1, …, xp – 1 и цикла вычисления последующих элементов xk . Доказательство правильности алгоритма строится на основе метода математической индукции или инварианта. Исследование эффективности алгоритма также несложно. Объем требуемой памяти определяется используемыми массивами. Трудоемкость определяется количеством выполнений операторов в цикле и обычно является линейной.

Пример 3.1. Вычисление элементов рекуррентной последовательности (c номера 0 по номер n), заданной формулой (3.1). Массив A с нумерацией элементов от 0 до p-1 содержит константы a0, a1, …, ap . Элементы последовательности будут за­ писываться в массив X. Функция f представляет собой формулу, в которой исполь­ зуются элементы массива X с номерами от k-p до k-1.

for k:=0 to p-1 do

X[k]:=A[k]; (3.1) for k:=p to n do

X[k]:= f(k,X);

Доказательство правильности этого алгоритма излишне, достаточно лишь убе­ диться в том, что алгоритм работает в строгом соответствии с определением рекур­ рентной последовательности.

Конец примера.

Заметим, что алгоритм (3.1) на самом деле является "полуфабрикатом", во вто­ рой строчке вместо f(k,X) надо записать конкретную формулу, зависящую от ве­ личин k,X[k-1],X[k-2],,X[k-p]. Если требуется вычислить не все n эле­

42

ментов последовательности, а только n–й, то можно обойтись без массива, исполь­ зуя p+1 или даже p вспомогательных переменных.

Пример 3.2. Сумму каких-либо элементов ai можно представить рекуррентной

последовательностью ранга 1:

 

 

 

 

 

ìS0

= 0,

 

 

(3.2)

í

= Si−1 + ai ,

i = 1, 2, ..., n .

 

îSi

 

Эта последовательность вычисляется алгоритмом:

 

 

 

 

 

 

 

 

S:=0;

 

 

 

 

 

for i:=1 to n do

 

 

(3.2)

 

S:=S+ a(i);

 

 

 

Здесь a(i) – формула вычисления i–го элемента суммы, зависящая только от i, поэтому достаточно всего одной дополнительной переменной S.

Конец примера.

Полином от x степени n можно представить в виде формулы Горнера:

P (x) = a

n

xn + a

n−1

xn−1 + ...+ a x + a

0

= (...

(a

n

x + a

n−1

)x + ...+ a )x + a

0

,

(3.3)

n

 

1

 

 

 

1

 

 

где an, an–1, …, a1, a0 – коэффициенты полинома.

Пример 3.3. Из формулы (3.3) можно получить следующее рекуррентное соотно­ шение для вычисления полинома при заданном значении x и коэффициентов a0, a1, …, an :

ìP0 = an ,

 

 

 

(3.4)

íP = P

x + a

n−i

, i = 1, 2, ..., n .

î i

i−1

 

 

 

В свою очередь, вычисления по рекуррентному соотношению (3.4) можно вы­ полнить алгоритмом:

 

 

 

P:=a[n];

 

(3.3)

for i:=1

to n do

P:=P*x+a[n-i];

 

Конец примера.

Формулу (3.3) можно трактовать как представление неотрицательного целого числа P(x) в системе счисления с целочисленным основанием x. Тогда алгоритм (3.3) позволяет вычислить значение числа при заданном основании x и наборе цифр a[n], a[n-1], …, a[0] в записи числа.

Пример 3.4. Из формулы (3.3) можно получить другое рекуррентное соотноше­ ние для вычисления цифр a0, a1, …, an неотрицательного целого числа Pn при задан­ ном значении основания x:

43

ìai = Pn−i mod x,

 

 

(3.5)

íP

= P

div x,

i = 0, 1, ..., n, P

> 0,

î n−i−1

n−i

 

n−i

 

 

где операция mod есть вычисление остатка от деления, а div – деление с отбрасы­ ванием остатка. При этом подразумевается, что количество цифр n в представлении числа Pn заранее не известно. Вычисления по рекуррентному соотношению (3.5) можно выполнить алгоритмом:

i:=0;

 

while P>0 do

 

begin

 

a[i]:=P mod x;

(3.4)

P:=P div x;

 

i:=i+1

 

end;

 

n:=i-1;

 

Конец примера.

Иногда удается вывести формулу, непосредственно выражающую n-й элемент рекуррентной последовательности. Доказательство правильности такой формулы можно произвести методом математической индукции.

Пример 3.5. Сумма квадратов чисел от 1 до n задается следующей рекуррент­ ной последовательностью:

ìS0

= 0,

2

 

 

(3.6)

í

= Sk −1 + k

,

k = 1, 2, ..., n ,

îSk

 

 

которая непосредственно вычисляется алгоритмом:

S:=0;

 

for k:=1 to n do

(3.5)

S:=S+k*k;

 

Как доказано в примере 2.3, для последовательности (3.6) справедлива форму­ ла (2.1):

Sn = 2n3 + 3n2 + n

6

Конец примера.

Пример 3.6. Числа Фибоначчи задаются следующей рекуррентной последова­ тельностью ранга 2:

ì f0

= 0, f1

= 1,

(3.7)

í

= fk −1 + fk −2 , k = 2, 3, ... ,

î fk

 

n–й элемент которой вычисляется следующим алгоритмом:

44

 

 

 

 

f1:=0; f2:=1;

 

 

for k:=2 to n do

 

 

begin

(3.6)

 

f3:=f2+f1;

 

 

 

f1:=f2; f2:=f3

 

 

end

 

Докажем, что для рекуррентной последовательности (3.7) справедлива формула

Муавра

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

æ

æ

 

+

 

 

 

ön

 

 

æ

 

 

 

 

 

 

 

 

ön

ö

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

fn

=

 

 

 

 

ç

ç1

 

÷

-

ç

1- 5 ÷

÷

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ç

ç

 

2

 

 

 

÷

ç

 

2

 

 

÷

÷ .

 

 

 

 

 

 

(3.8)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ç

è

 

 

 

 

ø

 

 

è

 

 

 

ø

÷

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5 è

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ø

 

 

 

 

 

 

 

Доказательство.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Базис. При n = 0 и n = 1 проверяем простой подстановкой в (3.8).

 

 

Предположение. Пусть при n ≥ 0 формула (3.8) верна.

 

 

 

 

 

 

 

Вывод. При n + 1 по формулам (3.7) и (3.8) получаем:

 

 

 

 

 

 

 

 

 

 

 

æ

æ

 

 

ön

 

 

æ

 

 

 

 

 

 

 

 

 

ön

ö

 

 

 

 

 

æ

æ

 

 

 

 

 

 

 

 

ön−1

æ

 

 

 

ön−1

ö

 

 

 

1

+

 

 

 

 

 

- 5

 

 

1

 

1 +

 

 

 

 

1

-

5

 

fn+1 =

 

ç

ç1

5 ÷

 

 

 

ç1

÷

 

÷

 

 

ç

ç

 

 

5 ÷

 

ç

÷

÷

 

 

 

ç

ç

2

÷

 

-

ç

 

 

2

 

 

 

÷

 

÷ +

 

 

 

 

ç

ç

2

÷

-

ç

 

2

 

÷

÷

=

 

 

5

ç

è

ø

 

 

 

è

 

 

 

 

 

ø

 

÷

 

 

 

5

 

ç

è

ø

 

è

 

 

ø

÷

 

 

 

è

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ø

 

 

 

 

è

 

 

 

 

 

 

 

 

 

 

ø

 

 

 

 

 

 

 

 

 

 

 

 

 

 

æ

æ

1+

 

 

 

ön+1

æ

 

 

 

 

 

 

 

ön+1 ö

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ç

ç

 

 

5 ÷

 

 

 

ç1-

÷

÷

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

=

 

 

 

ç

ç

 

 

 

 

2

÷

 

 

 

- ç

 

 

2

 

 

 

÷

÷ ,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5

ç

è

 

 

 

 

ø

 

 

 

è

 

 

 

 

 

ø

÷

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

è

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ø

 

 

 

 

 

 

 

учитывая, что

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

æ1±

 

 

 

5

ö

 

= 1+

1±

 

5

 

.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ç

 

 

2

 

 

 

÷

 

 

 

 

2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ç

 

 

 

 

 

÷

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

è

 

 

 

 

 

 

 

 

 

ø

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Конец примера.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

a = lim( fk+1 / fk ) = (1+

 

 

Число

 

5

) / 2 » 1,62 ,

входящее в формулу (3.8), называют

 

 

k→∞

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

золотым сечением. Заметим, что формулу (3.8) целесообразно применять при доста­ точно больших n. При этом вычисления необходимо выполнять над вещественны­ ми, т.е. приближенными числами, а результат округлять до целого. Кроме того, вто­ рое слагаемое по модулю меньше 0.5, и им можно пренебречь.

В некоторых случаях рекуррентная последовательность может иметь предел при n → ∞. Типичным примером могут служить бесконечные суммы, задаваемые соотно­ шениями:

ìS0

= 0,

(3.9)

í

= Sk −1 + fk , k = 1, 2, ...

îSk

 

45

в которых слагаемые f k стремятся к нулю при k → ∞. При этом сами слагаемые так­ же могут задаваться в виде рекуррентной последовательности:

 

ì f1

= a,

 

 

(3.10)

í

= p(k, fk −1 ),

k = 2, 3, ...

 

î fk

 

Вычисление предела такой

последовательности с заданной точностью eps

можно осуществить следующим алгоритмом:

 

 

 

 

 

 

 

 

 

S:=0; f:=a; k:=1;

 

 

 

 

while abs(f)>=eps do

 

 

 

begin

 

 

 

 

 

S:=S+f;

 

 

(3.7)

 

f:= p(k, f);

 

 

 

 

k:=k+1

 

 

 

 

end

 

 

 

 

Трудоемкость алгоритма, т.е. количество

k выполнений цикла, определяется из

формулы f k ≤ ε , где ε – заданная точность вычисления последовательности.

 

Пример 3.7. Приближенное значение функции

sin x можно вычислить с помо­

щью суммы

 

 

 

 

 

 

 

 

 

sin x = x -

x3

x5

k

x2k +1

 

 

+

 

- ...(-1)

 

 

,

(3.11)

3!

5!

(2k + 1)!

для которой справедливо рекуррентное соотношение (3.9). Поделив выражение для

fk = (-1)

k

x2k +1

 

на выражение для

fk−1 = (-1)k1

x2k −1

 

, получим рекуррентное

 

(2k

+ 1)!

(2k -1)!

 

 

 

 

 

 

 

 

 

 

 

соотношение для элементов суммы:

 

 

 

 

 

 

 

 

 

 

 

 

 

ì f

0

= x,

 

 

 

 

 

 

 

 

 

 

 

 

 

ï

 

 

 

x

2

 

 

 

 

(3.12)

 

 

 

 

 

í f

k

= - f

k −1

 

,

k = 1, 2, ...

 

 

 

 

 

 

 

 

 

 

 

 

ï

 

2k(2k +1)

 

 

 

 

 

 

 

 

 

 

î

 

 

 

 

 

 

 

 

Вычисление суммы (3.11) с заданной точностью eps записано в виде алгоритма (3.8). Завершимость алгоритма следует из того, что fk 0 при k → ∞. Доказатель­ ство правильности алгоритма легко провести методом инварианта.

S:=0; f:=x; k:=1;

while abs(f)>=eps do begin

S:=S+f; (3.8) f:=-f*x*x/(2*k*(2*k+1));

k:=k+1 end

 

 

 

 

 

 

 

 

 

 

 

46

Трудоемкость алгоритма (количество

k выполнений цикла) можно определить

из формулы

 

fk

 

=

 

x

 

2k −1

≤ ε , где ε –

заданная точность. Так, например, если |

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(2k −1)!

 

 

 

 

 

 

 

x| ≤ 1, то k < log2 1/ε .

 

 

Конец примера.

 

 

Для суммы со знакопеременными слагаемыми, как, например, в формуле (3.11), вычисленное значение суммы будет отличаться от истинного значения предела не бо­ лее чем на величину абсолютного значения первого отброшенного слагаемого. Если же все слагаемые имеют один и тот же знак, то для определения того, сколько слагае­ мых необходимо суммировать, приходится выводить формулу так называемого «остаточного члена суммы».

Более того, сумма, содержащая слагаемые одного и того же знака, может даже не иметь предела (т.е. быть бесконечной). Для вычисления такой суммы можно напи­ сать алгоритм, доказать его завершимость, однако на самом деле это не будет иметь никакого отношения к самой сумме.

Пример 3.8. Рассмотрим сумму

1

 

1

 

1

 

 

 

 

S = 1 +

+

+ ... +

+ ...

,

(3.13)

2

3

 

 

 

 

k

 

 

стремящуюся к бесконечности при k → ∞, хотя

lim

1

= 0

. Алгоритм ее вычисления:

 

 

 

 

 

 

k →∞ k

 

 

S:=0; k:=1;

while (1/k)>=eps do

begin

S:=S+1/k; (3.9) k:=k+1

end

конечен, причем вычисленное значение S будет также конечным. На самом деле S будет зависеть от eps. Например, при eps=10-4 вычисленное значение S9,79, а при eps=10-8 значение S≈19. Понятно, что вычислять подобные суммы не име­ ет никакого смысла.

Конец примера.

Вот еще несколько примеров рекуррентных последовательностей.

Пример 3.9. Рассмотрим рекуррентную последовательность для вычисления квадратного корня заданного числа, которая была известна еще Герону:

47

 

 

 

 

 

 

 

 

 

 

 

ìs0

= 1,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ï

 

 

 

 

 

 

1

æ

 

 

 

 

a

ö

 

 

 

 

 

 

 

 

 

 

 

 

 

(3.14)

 

 

 

 

 

 

 

 

 

 

í

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ï

s

k

=

 

ç s

k −1

+

 

 

 

 

 

÷, k = 1, 2, ...

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2

ç

 

 

 

 

 

÷

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

î

 

 

 

 

 

 

è

 

 

 

 

sk −1 ø

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

lim sk =

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Докажем, что

 

a

при a ≥ 0.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

k→∞

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Доказательство. Пусть sk =

 

 

 

 

+ ek , где ek – ошибка k–го приближения. Тогда

 

 

a

 

 

 

1 æ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

a

 

 

 

ö

 

 

 

 

1

 

 

 

 

e2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

s

k

=

ç

 

a + e

k −1

+

 

 

 

 

 

 

 

 

 

 

 

÷ =

 

 

a +

 

 

 

×

 

 

 

 

k−1

 

=

 

a + e

k

.

(3.15)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ç

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

÷

 

 

 

 

2

 

 

 

 

a + ek −1

 

 

 

 

 

 

 

 

2 è

 

 

 

 

 

 

 

 

 

 

 

a + ek −1 ø

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Вычислим e1:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

= 1 (1+ a) -

 

=

(

 

-1)2

 

 

 

 

 

 

 

 

 

 

e = s -

 

 

 

 

a

³ 0 ,

 

 

 

 

 

 

 

 

 

a

a

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

1

 

 

 

 

 

 

 

 

 

2

 

 

 

 

 

 

 

 

 

 

 

 

 

2

 

 

 

 

 

 

 

 

 

и тогда

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ek =

1

 

 

 

 

 

e2

 

³ 0,

ek £

1

×ek −1,

k = 2, ...

 

 

 

 

 

 

 

2 ×

 

 

a + ek −1

2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

k

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Полученные неравенства утверждают, что, начиная с

e1,

все ek

 

неотрицатель­

ны, и каждая последующая ошибка меньше предыдущей, по крайней мере, в 2 раза. Т.е. e1/e2 ≥ 2, e1/e3 ≥ 4, …, e1/ek ≥ 2k – 1, откуда:

k £ log2

1+ a .

(3.16)

 

2ε

 

Поэтому limk→∞ ek = 0 . Кроме того, начиная с s1 , все sk убывают.

Вычисление предела последовательности (3.14), начиная с s1 , с заданной точно­ стью eps записано в виде алгоритма (3.10).

s:=(1+a)/2; e:=eps;

 

while e>=eps

do

 

begin s1:=(s+a/s)/2;

(3.10)

e:=s-s1;

s:=s1

 

end

 

 

Трудоемкость алгоритма, т.е. количество выполнений цикла, определяется фор­ мулой (3.16).

Конец примера.

Пример 3.10. Пусть на интервале [a, b] задана непрерывная функция y = f(x), причем значения функции на концах интервала f(a) и f(b) имеют разные знаки. Тогда на этом интервале функция имеет хотя бы один корень x0 , такой, что f(x0) = 0. Задача состоит в вычислении корня функции с заданной точностью ε / 2 , т.е. требуется найти такое z, что | x0 z | ≤ ε / 2 . Дихотомический метод решения этой задачи состоит в том, что на каждом шаге интервал делится пополам и опреде­

48

ляется тот из полуинтервалов, где может находиться корень. Можно построить сле­ дующую рекуррентную последовательность пар чисел {ui , vi}:

ìu0 = a, v

ïíui+1 = ui , ïîui+1 = (ui

0 = b,

 

 

 

vi+1 = (ui + vi ) / 2,

при

signf (ui ) ¹ signf ((ui + vi ) / 2),

(3.17)

+ vi ) / 2, vi+1 = vi ,

при

signf (ui ) = signf ((ui + vi ) / 2),

 

которая сходится к искомому корню. Алгоритм (3.11) вычисляет ее предел x с за­ данной точностью eps/2.

u:=a; v:=b; x:=(u+v)/2; while (v-u)>=eps do

begin

if f(u)*f(x)<=0 then v:=x (3.11) else u:=x;

x:=(u+v)/2 end

Трудоемкость алгоритма, т.е. количество выполнений цикла, определяется фор­ мулой:

é

b - a ù

(3.18)

k = êlog2

ε

ú .

ê

ú

 

Конец примера.

Пример 3.11. Алгоритм Евклида, вычисляющий наибольший общий делитель НОД(a, b) двух целых чисел a ≥ 0, b ≥ 0, основан на следующих инвариантных со­ отношениях:

1)НОД(a, 0) = a;

2)НОД(a, b) = НОД(b, a);

3)НОД(a, b) = НОД(a mod b, b), a b > 0.

Первые два соотношения очевидны. Докажем третье. Во-первых, заметим, что операция r = a mod b эквивалентна многократному вычитанию b из a, до тех пор,

пока не будет выполняться: 0 ≤ r < b. Поэтому достаточно доказать, что НОД(a, b) = НОД(a b, b), a b > 0.

Пусть верно противоположное утверждение, а именно:

 

 

 

НОД(a, b) = d,

НОД(a b, b) = c, причем c > d.

 

 

Но тогда

a − b

=

a

- b , при этом

 

a − b

, а также b – оба целые, в то время как

a

 

c

c

 

c

c

 

 

c

 

c

может быть целым лишь в случае, если

c = d. Это противоречие и доказывает третье

соотношение.

 

 

 

 

 

 

 

 

где fk , fk – 1

49

Из этих соотношений можно построить такую последовательность пар {xi, yi},

что: НОД(xi, yi) = НОД(a, b), max(xi, yi) > max(xi + 1, yi + 1):

 

 

 

 

 

 

ìx0 = a, y0 = b,

 

 

 

 

 

 

 

 

 

 

 

 

ï x

i+1

= x

i

mod y

,

y

i+1

= y

,

при

x

i

³ y

i

> 0

(3.19)

ïí

 

i

 

 

i

 

 

 

 

 

 

î yi+1

= yi mod xi ,

xi+1

= xi ,

при

yi

³ xi

> 0,

 

Алгоритм (3.12) вычисляет последовательность (3.19) до тех пор, пока ее можно продолжать (пока обе переменных, как x, так и y, положительны).

x:=a; y:=b;

while (x>0)and(y>0) do

if x>=y then x:=x mod y (3.12) else y:=y mod x;

if x>0 then nod:=x else nod:=y

Трудоемкость алгоритма, т.е. количество выполнений цикла, определяется чис­ лами a и b. При этом наихудший случай будет, если при

fk ≥ max(a, b) > fk – 1,

– числа последовательности Фибоначчи, выполняется условие max(a, b) = fk , min(a, b) = fk – 1 .

Тогда k – количество выполнений цикла. Пренебрегая вторым слагаемым в (3.8) и логарифмируя левую и правую части, получим:

k » logα (5 fk ) = logα 2×log2 (5 fk ) ,

k £ logα 2 × log2 (5 max(a,b)) » 1.4 × log2 max(a,b) ,

где a = (1+ 5) / 2 .

Конец примера.

3.2 Алгоритмы с упорядоченными массивами

Числовой массив с расположенными по неубыванию или по невозрастанию зна­ чениями элементов называется упорядоченным. Использование таких массивов поз­ воляет создавать эффективные алгоритмы решения многих важных задач. Алгорит­ мы упорядочения (называемые также алгоритмами сортировки) позволяют превра­ тить неупорядоченный массив в упорядоченный. Примерами таких алгоритмов яв­ ляются алгоритмы (1.8), (2.6)–(2.8).

Задача слияния двух входных упорядоченных массивов A и B состоит в фор­ мировании упорядоченного массива C, содержащего все элементы из входных мас­

50

сивов. Можно переписать все элементы из массивов A и B подряд, а затем все упо­ рядочить, однако задачу слияния можно решить более эффективно.

Рассмотрим следующий алгоритм слияния для упорядоченных по неубыванию массивов. Вначале элемент A[1] сравнивается с элементом B[1] и меньший из них переписывается в массив C. Если меньшим был A[1], то на следующем шаге сравниваются A[2] и B[1], а если меньшим был B[1], то будут сравниваться A[1] и B[2] и т.д. При равенстве можно пересылать любой элемент. Если на оче­

редном шаге окажется, что из одного входного массива все элементы переписаны в C, то переписывается элемент из другого массива.

Пример 3.12. Алгоритм слияния. Входные массивы A и B содержат соответ­ ственно n1 и n2 элементов, результирующий массив C будет содержать n1+n2 элементов.

 

i1:=1; i2:=1;

{1}

 

 

 

for i:=1 to n1+n2 do

{2}

 

 

 

if i1>n1 then

{3}

 

 

 

begin

C[i]:=B[i2]; i2:=i2+1 end

{4}

 

 

 

else if

i2>n2 then

{5}

(3.13)

 

begin

C[i]:=A[i1]; i1:=i1+1 end

{6}

 

else if

A[i1]<=B[i2] then

{7}

 

 

 

begin

C[i]:=A[i1]; i1:=i1+1 end

{8}

 

 

 

else

C[i]:=B[i2]; i2:=i2+1 end

{9}

 

 

 

begin

{10}

 

 

Доказательство правильности. Инвариантом цикла будет следующее логиче­

ское соотношение:

 

 

 

 

 

{C[1],...,C[i-1]}={A[1],...,A[i1-1],B[1],...,B[i2-1]},

 

 

C[1]<=C[2]<=...<=C[i-1],

 

i2<=n2.

 

C[i-1]<=A[i1] при i1<=n1, C[i-1]<=B[i2] при

Первая строчка в логическом соотношении означает, что набор значений элемен­

тов, записанных (на очередном шаге) в массив

C, совпадает с набором значений

элементов, переписанных из массивов A и B (но в другом порядке).

Завершимость алгоритма очевидна, цикл выполнится ровно n1+n2 раз, после его выполнения i=n1+n2+1. Если инвариант справедлив, то после выполнения

цикла в n1+n2 элементах массива C окажутся упорядоченные значения всех эле­ ментов из A и B.

Для доказательства инварианта необходимо проверить его истинность перед вы­ полнением цикла, а также истинность при однократном выполнении цикла. Второе доказывается рассмотрением четырех случаев:

1) условие в строчке 3 алгоритма истинно;