Оценка сложности алгоритма

Тема 1

Оценка сложности алгоритма (сравнение сортировки обменом и пузырьком). Поиск в упорядоченном и неупорядоченном массиве. Оценка сложности поиска для дихотомического алгоритма. Рекурсивные алгоритмы.

Поиск в упорядоченном и неупорядоченном массивах

Дихотомия

 

Задача 1.

Дана произвольная целочисленная таблица из n элементов. Определить, есть ли в таблице элемент, равный числу L.

Вопрос: Как осуществить поиск?

Сравнивать L со всеми элементами таблицы, пока не найдем равный ему.

 

Тесты: 2 ситуации:

Такие элементы есть: k := номер элемента, равного L

Такого элемента нет: k:=0

Задача 2.

Дана целочисленная возрастающая таблица из n элементов. Определить, есть ли в таблице элемент, равный числу L.

Допустим, что в таблице нет повторяющихся элементов

Например:

2 3 5 7 8 10 11 15 16

1 2 3 4 5 6 7 8 9

Найти: А[к]=L

к - результат

Тестирование

Какие ситуации возможны на нахождение элемента в таблице ?

Какие значения может принять величина к после исполнения алгоритма?

К= {1,..,9} или к=0

Число есть

На границе в середине

 

Числа нет

 

За границами между элементами

интервала ед. длины (соседними)

L<A[1] или L>A[n] Ak <L<Ak+1

k+1 – k = 1

интервал [k, k+1] – ед. длины

 

Как осуществить поиск в упорядоченном массиве?

1) Методом из 1 задачи (перебор всех подряд идущих элементов)

Это долго, если чисел много

2) Десятками с возвратом или через 1 элемент

3) Более короткий способ: брать число c номером из середины интервала [1, n].

Действия:

1 – Берем число из середины интервала

повторять: 2 – Выбираем интервал: либо левый, либо правый, - тот, в котором

находится искомое число.

 

До каких пор производить 1 и 2 действия?

Пока не найдем число, т. е. В = А[к] и к – результат либочисла нет, т. к. он между соседними элементами, т. е. длина интервала стала < 1.

Дихотомия

Имеем возрастающий массив. А[1] < A[2] < …….<A[n]

[.a . . . .k][ k+1. . . .b]

интервал [1, n] или [a, b]

Берем число посередине с индексом k.

Сравниваем L и A[k]

т. е., проверяем, в какой интервал попало число L

левый L Î [1, k] При каком условии? L <= A[k] Меняем границы интервала [a, b] меняется правая граница b:=k Правый L Î [k + 1, n] При каком условии? L <= A[k] Меняем границы интервала [a, b] меняется левая граница a:=k+1

 

Так как границы поменяли, берем элемент, который находится в середине нового интервала

Поменять индекс к в обоих случаях:

k – середина [а, b]

k=( a + b )/2

 

Многократные действия: (поиск элемента)

1) Сравнение L и A[к]

2) Смена границ

3) Смена середины

Условия продолжения

До каких пор, пока L< > A[k] иотрезок ед. длины b – a>=1

В каком случае закончит выполняться цикл?

Когда условие нарушается. В каких случаях условие нарушается?

1. L = A[k] либо 2. b – a < 1

тогда к – искомый результат тогда к – получено какое – то

число, но A[k] < > L

После поиска надо сделать проверку полученного индекса k.

Если A[k] – не искомое число, то k:=0.

Но k может быть равно 0 и по другой причине, если число находится за границами интервала.

Замечание:прежде, чем делать поиск, следует проверить наличие числа в интервале [A[1], A[n]]

если нет Þ к:=0

если есть Þ поиск (n, A, L, k)

 

Величины:

n - кол-во элементов

А – таблица

L – заданное число

К – искомый номер.

 


Описание алгоритма

Проверка L Î [А[1], А[n]]

 

 

нет да

к:=0 поиск (n, A, L, k)

ß

проверка найденного числа k

 

нет да

k:=0 k– искомое

 

Начальные условия

а = 1 границы первого интервала

b = n

 

k = ( a + b)/ 2 - середина в первом интервале [а, b]

n = 9 (для тестового примера)

 

Тестирование

Для таблицы

 

элемент
№ эл-та

 

L = 1, 17 – за пределами

2, 16 – на границе первого интервала

 

 

L = 3, 15 – левый и правый интервал

8 – посередине первого интервала

12, 4 – между соседями

Оценка сложности алгоритма

Пусть N – размерность просматриваемого массива.

Сколько раз рассматриваем интервалы при делении отрезка пополам? р= Log2 N

Следовательно, учитывая среднее выполнение операций, имеем С *Log2 N = О(Log2 N)

Рекурсия

Иногда оказывается более удобным свести задачу к другой, более простой задаче, а иногда - свести задачу к ней же самой, упростив исходные данные.

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

Способ сведения задачи к ней же самой, но с измененными исходными данными, называется рекурсией. Алгоритм решения задачи, который содержит обращения к себе, называется рекурсивным алгоритмом.

Основой для разработки рекурсивных алгоритмов могут служить рекуррентные соотношения (формулы) устанавливающие зависимости между результатами каких-либо действий (операций) на n - ом шаге от результатов аналогичных действий (операций), полученных на предыдущем n-1 - м шаге.

При составлении рекурсивной программы встает, по крайней мере, четыре проблемы:

1. завершение программы;

2. верность алгоритма;

3. быстродействие;

4. глубина рекурсии.

Обсудим пути их решения.

1. Подобно операторам цикла рекурсия может привести к не заканчивающимся вычислениям, поэтому рекурсивное обращение к процедуре должно управляться некоторым условием. Необходимо убедится, что с каждым вызовом рекурсивной процедуры параметр, входящий в условие окончания рекурсии, изменяется по определенному закону и что множество, составленное из значений этого параметра в соответствии с данным законом, конечно, т.е. рано или поздно условие окончания выполнится.

2. Чтобы проверить верность программы, содержащей рекурсивный вызов, нужно составить цепочку вызовов и проследить ее от конца к началу или придумать соответствующий инвариант.

3. Третья проблема - быстродействие. Другими словами, следует оценить, целесообразно ли использовать рекурсию в данном случае или лучше воспользоваться эквивалентной не рекурсивной программой. Рекурсивные программы короче, но менее быстродействены.

4. Наконец четвертая проблема. Важно убедиться, что максимальная глубина рекурсии не только конечна, но и достаточно мала, так как каждая рекурсивная активизация процедуры требует памяти для размещения переменных. Кроме того, нужно еще сохранять текущее состояние вычислений, чтобы можно было вернуться в него по окончании новой рекурсивной активизации процедуры. Необходимо определить, не превысит ли используемая рекурсивной программой память возможностей ЭВМ.

Примеры задач: факториал, Ханойские башни