Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции Анализ алгоритмов.doc
Скачиваний:
55
Добавлен:
20.11.2018
Размер:
1.29 Mб
Скачать

Глава 8 Теорема о сложности рекурсивных программ

Теорема. Пусть a, b, c неотрицательные постоянные. Решение рекуррентного уравнения

,

где n – степень числа c, имеет вид:

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

Если n – степень числа c, то T(n) =, где .

Если a<c, то сходиться и, следовательно, T(n) = O(n).

Если a=c, то каждый член этого ряда равен единице, а всего в нем О(log n) членов. Поэтому T(n) = O(nlog n).

Если a>c, то , что составляет , или .

Из теоремы вытекает, что разбиение задачи размера n ( за линейное время) на две подзадачи размера n/2 дает алгоритм сложности O(nlog n).

Из теоремы вытекает, что разбиение задач размера n (за линейное время) на две подзадачи размера n/2 дает алгоритм сложности O(n log n). Если бы подзадач было 3,4,8, то получился бы алгоритм сложности nlog3,n2, n3 соответственно.

С другой стороны, разбиение задачи на 4 подзадачи размера n/4 дает алгоритм сложности O(n log n), а на 9 и 16 – порядка nlog3, n2 соответственно. Поэтому асимптотически более быстрый алгоритм умножения целых чисел (см. ниже) можно было бы получить, если бы удалось так разбить исходные целые числа на 4 части, чтобы суметь выразить исходное умножение через 8 или менее умножений. Другой тип рекуррентных соотношений возникает в случае, когда работа по разбиению задачи не пропорциональна ее размеру.

Если n не является степенью числа c, то ,обычно, можно вложить задачу размера n в задачу размера n, где n – наименьшая степень с, большая или равная n. Поэтому порядки роста, указанные в теореме, сохраняются для любого n. на практике часто можно разработать рекурсивные алгоритмы, разбивающие задачи произвольного размера на c равных частей, где с велико, насколько возможно. Эти алгоритмы, как правило, эффективней (на постоянный множитель) тех, которые получаются путем представления размера входа в виде ближайшей сверху степени числа c.

Пример 1. Нахождение наибольшего и наименьшего элементов.

Вход: множество S из n элементов, где n степень числа 2, n2.

Выход: наибольший и наименьший элементы множества S.

Метод: К множеству S применяется рекурсивная процедура MAXMIN. Она имеет один аргумент, представляющий собой множество S, такое, что |S|=2k при некотором k1, и вырабатывает пару (а, b), где а – наибольший и b – наименьший элемент в S.

Procedure MAXMIN(S);

  1. if |S|=2 then

begin

  1. пусть S={a,b};

  2. return (MAX(a,b),MIN(a,b))

end

else

begin

  1. Разбить S на два равных полмножества S1 и S2;

  2. (MAX1,MIN1):=MAXMIN(S1);

  3. (MAX2,MIN2):=MAXMIN(S2);

  4. return (MAX(MAX1,MAX2),MIN((MIN1,MIN2)

end.

Заметим, что сравнение элементов множества S происходит только на шаге 3, где сравниваются два элемента множества S, из которых оно состоит, и на шаге 7, где сравниваются MAX1 с MAX2 и MIN1 с MIN2. Пусть T(n) – число сравнений элементов множества S, которые надо произвести в процедуре MAXMIN, чтобы найти наибольший и наименьший элементы n-элементного множества.

Ясно, что Т(2)=1. Если n>2, то T(n) – общее число сравнений, произведенных в двух вызовах процедуры MAXMIN (стоки 5 и6),работающих на множествах размера n/2 и еще два сравнения в строке 7. таким образом

(1)

Решение рекуррентных уравнений (1) служит функция T(n)=n-2. Легко проверить, что эта функция удовлетворяет (1) при n=2, и если она удовлетворяет (1) при n=m, то

T(2m)=2 (-2)+2=(2m)-2,

т.е. она удовлетворяет (1) при n=2m. Таким образом, индукцией доказано, что T(n)= -2 удовлетворяет (1), если n степень 2.

Докажем, что для одновременного нахождения наибольшего и наименьшего элементов n-элементного множества надо сделать не менее -2 сравнений его элементов.

Различные стадии работы такого алгоритма над n-элементным множеством опишем четверкой (a,b,c,d), где a элементов вообще не сравнивались, b элементов участвовали в сравнениях и всегда были большими, c элементов участвовали в сравнениях и всегда были меньшими, d элементов участвовали в сравнениях и были как большими так меньшими. Начальная конфигурация характеризуется – (n,0,0,0); конечная – (0,1,1,n-2). Из конфигурации (a,b,c,d) можно перейти либо в себя, либо в

(a-2, b+1, c+1, d), если а2;

(a-1, b, c+1, d) или (a-1,b+1,c,d), если а1;

(a, b-1, c d+1), если b2;

(a, b, c-1, d+1), если c2.

Отсюда следует, что требуется +b+c-2 сравнений, чтобы из (a, b, c, d) получить (0, 1, 1,a+b+c+d-2).

Пример 2. Умножение двух n-разрядных двоичных чисел.

Традиционный метод требует О(n2) , битовых операций. Рассмотрим метод, в котором достаточно битовых операций порядка .

Пусть x и y – два n-разрядных двоичных чисел. Снова будем считать для простоты, что n есть степень 2. Разобьем

y = y =

Если рассматривать каждую из этих частей как (n/2)-разрядные число, то можно представить произведение z=xy в виде

z=(a2n/2+b)(c2n/2+d)=ac2n+(ad+bc)2n/2+bd (1)

Равенство (1) дает способ вычисления произведения xy с помощью четырех умножений (n/2)-разрядных чисел и нескольких сложений и сдвигов (умножение на степень 2). Произведение z чисел x и y также можно вычислить по следующей программе:

begin

u:=(a+b)(c+d); u:=(a+b)(c+d);

v

(2)

:=ac;

w:=bd;

z:= v2n+(u-v-w)2n/2+w

end

На время забудем, что из-за переноса a+b и c+d могут иметь n/2+1 разрядов, и предположим, что они состоят из n/2 разрядов. Наша схема для умножения двух n-разрядных чисел требует только трех умножении (n/2)-разрядных чисел и нескольких сложений и сдвигов. Для вычисления произведений u,v и w можно применить эту программу рекурсивно. Сложение и сдвиги занимают О(n) времени. Следовательно, временная сложность умножения двух n-разрядных чисел ограничена сверху функцией

(3)

где k – постоянная, отражающая сложение и сдвиги в выражениях, входящих в (2).

Решение рекуррентных уравнений (3) ограничено сверху функцией

3k3.

В самом деле, покажем, что T(n)=3k-2kn. Применим индукцию по n.

n=1 – тривиально.

Если функция T(n)=3k-2kn удовлетворяет (3) при n=m, то

T(2m)=3T(m)+2km=3[3k-2km]+2km=3k-2k(2m).

Так, что она удовлетворяет (3) и при n=2m. Отсюда следует, что T(n)3k.

Замечание. Попытка использовать в индукции 3k вместо 3-2кn не проходит.

Для завершения описания алгоритма умножения мы должны учесть, что числа a+b и c+d, вообще говоря, имеют n/2+1 разрядов, и поэтому произведение (a+b)(c+d) нельзя вычислить непосредственно применением нашего алгоритма к задаче размера n/2. вместо этого надо записать a+b в виде а12n/2+b1, где а1 равно 0 или 1. Аналогично запишем c+d с12n/2+d1/ тогда произведение (a+b)(c+d) можно представить в виде

а1c12n+(a1d1+b1 c1)2n/2+b1d1. (4)

Слагаемое b1d1 вычисляется с помощью рекурсивного применения нашего алгоритма умножения к задаче размера n/2. остальные умножения в (4) можно сделать за время О(n), поскольку они содержит в качестве одного из аргументов либо единственный бит а1 или с1, либо степень числа 2.

Упражнение. Решите следующие рекуррентные уравнения, считая, что T(1)=1:

  1. T(n)=aT(n-1)+ bn

  2. T(n)=aT(n/2)+ bnlog2n

  3. T(n)=aT(n-1)+ bnc

  4. T(n)=aT(n/2)+ bnc