- •20 Понятие качества программного средства.
- •13. Алгоритмы поиска, выборки и сортировки.
- •12. Основные вопросы анализа алгоритмов. Понятие алгоритмической сложности.
- •19 Специфика разработки программных средств.
- •18. Жизненный цикл программного средства.
- •17 Программа как формализованное описание процесса обработки данных.
- •16.Методы разработки алгоритмов. Метод декомпозиции.
- •14.Алгоритмы решения задач на графах.
Алгоритмы
поиска:
алгоритм последовательного поиска
(АПП); алгоритм двоичного поиска (АДП). АПП: Алгоритм
последовательного поиска последовательно
просматривает по одному элементу
списка, начиная с первого, до тех пор,
пока не найдет целевой элемент. Очевидно,
что чем дальше в списке находится
конкретное значение ключа, тем больше
времени уйдет на его поиск. Это
следует помнить при анализе алгоритма
последовательного поиска. Вот как
выглядит полный алгоритм последовательного
поиска. SequentialSearch(list.target,N) list
список для просмотра target
целевое значение N
число элементов в списке for
i=l
to
N
do if
(target=list[i]) return
i end
if end
for return
0 Анализ
наихудшего случая У
алгоритма последовательного поиска
два наихудших случая. В первом случае
целевой элемент стоит в списке последним.
Во втором его вовсе нет в списке. A(N)=
N, где N
- - число
элементов в списке.
Анализ
среднего случая Для
алгоритмов поиска возможны два средних
случая. В первом предполагается, что
поиск всегда завершается успешно, во
втором - что иногда целевое значение в
списке отсутствует. A(N)=(n+2)/2 АДП: При
сравнении целевого значения со средним
элементом отсортированного списка
возможен один из трех результатов:
значения равны, целевое значение меньше
элемента списка, либо целевое значение
больше элемента списка. В первом, и
наилучшем, случае поиск завершен. В
остальных двух случаях мы можем отбросить
половину списка. Когда целевое значение
меньше среднего элемента, мы знаем, что
если оно имеется в списке, то находится
перед этим средним элементом. Когда же
оно больше среднего элемента, мы знаем,
что если оно имеется в списке, то
находится после этого среднего элемента.
Этого достаточно, чтобы мы могли одним
сравнением отбросить половину списка.
При повторении этой процедуры мы сможем
отбросить половину оставшейся части
списка.
В
наихудшем случае число проходов равно
k = log2(N
+1). Средний
случай A(N)≈
log2(N
+1)-1
Алгоритм
выборки. Иногда
нам нужен элемент из списка, обладающий
некоторыми специальными свойствами,
а не имеющий некоторое конкретное
значение. Другими словами, вместо записи
с некоторым конкретным значением поля
нас интересует, скажем, запись с
наибольшим, наименьшим или средним
значением этого поля. В более общем
случае нас может интересовать запись
с К-ым по
величине значением поля. Один из способов
найти такую запись состоит в том, чтобы
отсортировать список в порядке убывания;
тогда запись с К-ым
по величине
значением окажется на К-ом
месте. На
это уйдет гораздо больше сил, чем
необходимо: значения, меньшие искомого,
нас, на самом деле, не интересуют. Может
пригодиться следующий подход: мы находим
наибольшее значение в списке и помещаем
его в конец списка. Затем мы можем найти
наибольшее значение в оставшейся части
списка, исключая уже найденное. В
результате мы получаем второе по
величине значение списка, которое можно
поместить на второе с конца место в
списке. Повторив эту процедуру К
раз, мы найдем
К-ое по
величине значение. В результате мы
приходим к алгоритму АВПК:
FindKthLargest(list,K,N) list
список для просмотра N
число элементов в списке К
порядковый номер по величине требуемого
элемента for
i=l to К
do largest=list[1] largestLocation=l for
j=2 to N-(i-l) do if
list [j]>largest then largest=list
[j] largestLocation=j end
if end
for SwapQist
[N-(i-l)] .list [largestLocation] ) end
for return
largest
Сложность:
A(N)= NK- (K(K-1))/2
Сортировка
вставками. Основная
идея сортировки вставками состоит в
том, что при добавлении нового элемента
в уже отсортированный список его стоит
сразу вставлять в нужное место вместо
того, чтобы вставлять его в произвольное
место, а затем заново сортировать весь
список. Сортировка вставками считает
первый элемент любого списка
отсортированным списком длины 1.
Двухэлементный отсортированный список
создается добавлением второго элемента
исходного списка в нужное место
одноэлементного списка, содержащего
первый элемент. Теперь можно вставить
третий элемент исходного списка в
отсортированный двухэлементный список.
Этот процесс повторяется до тех пор,
пока все элементы исходного списка не
окажутся в расширяющейся отсортированной
части списка.
Наихудший
случай: A(N)=O(N2/2)
Средний
случай: A(N)=
O(N2/4)
Пузырьковая
сортировка. Основной
принцип состоит в выталкивании маленьких
значений на вершину списка в то время,
как большие значения опускаются вниз.
У пузырьковой сортировки есть много
различных вариантов. Алгоритм пузырьковой
сортировки совершает несколько проходов
по списку. При каждом проходе происходит
сравнение соседних элементов. Если
порядок соседних элементов неправильный,
то они меняются местами. Каждый проход
начинается с начала списка. Сперва
сравниваются первый и второй элементы,
затем второй и третий, потом третий и
четвертый и так далее; элементы с
неправильным порядком в паре
переставляются. При обнаружении на
первом проходе наибольшего элемента
списка он будет переставляться со всеми
последующими элементами пока не дойдет
до конца списка. Поэтому при втором
проходе нет необходимости производить
сравнение с последним элементом. При
втором проходе второй по величине
элемент списка опустится во вторую
позицию с конца. При продолжении процесса
на каждом проходе по крайней мере одно
из следующих по величине значений
встает на свое место. При этом меньшие
значения тоже собираются наверху. Если
при каком-то проходе не произошло ни
одной перестановки элементов, то все
они стоят в нужном порядке, и исполнение
алгоритма можно прекратить. Стоит
заметить, что при каждом проходе ближе
к своему месту продвигается сразу
несколько элементов, хотя гарантированно
занимает окончательное положение лишь
один.
наилучший
случай: A(N)=N-1
наихудший
случай: A(N)=O(N2/2)
средний
случай: A(N)=O(N2/3)
Сортировка
Шелла. Сортировку
Шелла придумал Дональд Л. Шелл. Ее
необычность состоит в том, что она
рассматривает весь список как совокупность
перемешанных подсписков. На первом
шаге эти подсписки представляют собой
просто пары элементов. На втором шаге
в каждой группе по четыре элемента. При
повторении процесса число элементов
в каждом подсписке увеличивается, а
число подсписков, соответственно,
падает. На рис. 3.1 изображены подсписки,
которыми можно пользоваться при
сортировке списка из 16 элементов. На
рис. 3.1 (а) изображены восемь подсписков,
по два элемента в каждом, в которых
первый подсписок содержит первый и
девятый элементы, второй подсписок —
второй и десятый элементы, и так далее.
На рис. 3.1 (б) мы видим уже четыре подсписка
по четыре элемента в каждом.
Продолжение
на следующей стороне
Первый
подсписок на этот раз содержит первый,
пятый, девятый и тринадцатый элементы.
Второй подсписок состоит из второго,
шестого, десятого и четырнадцатого
элементов. На рис. 3.1 (в) показаны два
подсписка, состоящие из элементов с
нечетными и четными номерами
соответственно. На рис. 3.1 (г) мы вновь
возвращаемся к одному списку. Сортировка
подсписков выполняется путем однократного
применения сортировки вставками из §
3.1. В результате мы получаем следующий
алгоритм: Shellsort(list.N) list
сортируемый список элементов N
число элементов в списке passes
= [log_2
N] while
(passes>=l) do increment=2*passes-1 for
start=l to increment do InsertionSort(list,N,start,increment) end
for passes=passes-l end
while Переменная
increment содержит величину шага между
номерами элементов подсписка. (На рис.
3.1 шаг принимает значения 8, 4, 2 и 1.) В
алгоритме мы начинаем с шага, на 1
меньшего наибольшей степени двойки,
меньшей, чем длина элемента списка.
Поэтому для списка из 1000
элементов первое значение шага равно
511. Значение шага также равно числу
подсписков. Элементы первого подсписка
имеют номера 1 и 1+increment; первым элементом
последнего подсписка служит элемент
с номером increment. При последнем исполнении
цикла while значение переменной passes будет
равно 1, поэтому при последнем вызове
функции InsertionSort значение
переменной increment будет равно 1. Анализ
этого алгоритма основывается на анализе
внутреннего алгоритма InsertionSort. (в
наихудшем случае сортировка вставками
списка из N
элементов
требует (N2-N)/2
операций, а
в среднем случае она требует N2/4
элементов.) В
основе сортировки Шелла лежит сортировка
вставками. Сортировка Шелла — это один
общий вид сортировки, однако выбор
параметров может существенно повлиять
на ее эффективность. В
наихудшем случае A(N)=N3/2
Корневая
сортировка. При
корневой сортировке упорядочивание
списка происходит без непосредственного
сравнения ключевых значений между
собой. При этом создается набор “стопок”,
а элементы распределяются по стопкам
в зависимости от значений ключей. Собрав
значения обратно и повторив всю процедуру
для последовательных частей ключа, мы
получаем отсортированный список. Чтобы
такая процедура работала, распределение
по стопкам и последующую сборку следует
выполнять очень аккуратно. Подобная
процедура используется при ручной
сортировке карт. В некоторых библиотеках
в докомпьютерные времена при выдаче
книги заполнялась карта выдачи. Карты
выдачи были занумерованы, а сбоку на
них проделывался ряд выемок --в зависимости
от номера карты. При
возвращении книги карта выдачи удалялась,
и ее клали в стопку. Затем вся стопка
прокалывалась длинной иглой на месте
первой выемки, и иглу поднимали. Карты
с удаленной выемкой оставались на
столе, а остальные были наколоты на
иглу. Затем две получившиеся стопки
переставлялись так, что карточки на
игле шли за карточками с отверстиями.
Затем игла втыкалась в месте второй
выемки и весь процесс повторялся. После
прокалывания по всем позициям карты
оказывались в порядке возрастания
номеров. Эта процедура ручной обработки
разделяет карты по значению младшего
разряда номера на первом шаге и старшего
разряда на последнем.
Если
в самом длинном ключе М
цифр, а число
ключей равно N,
то сложность
корневой сортировки имеет порядок
O(MN). Однако
длина ключа невелика по сравнению с
числом ключей. Например, при ключе из
шести цифр число различных записей
может быть равно миллиону. Длина ключа
не играет роли и алгоритм имеет линейную
по длине сложность O(N).
Это очень
эффективная сортировка, поэтому
возникает вопрос, зачем вообще нужны
какие-либо другие методы. Ключевым
препятствием в реализации корневой
сортировки служит ее неэффективность
по памяти.
Быстрая
сортировка. Быстрая
сортировка — еще один рекурсивный
алгоритм сортировки. Выбрав элемент в
списке, быстрая сортировка делит с его
помощью список на две части. В первую
часть попадают все элементы, меньшие
выбранного, а во вторую — большие
элементы. Алгоритм Quicksort действует
по-другому, поскольку он применяется
рекуррентно к обеим частям списка. В
среднем такая сортировка эффективна,
однако в наихудшем случае ее эффективность
такая же, как у сортировки вставками и
пузырьковой. сложность
– в среднем О
( n
log2
n)
и О(n2)
в худшем случае.
Сортировка
слиянием. Сортировка
слиянием — первая из встречающихся
нам рекурсивных сортировок. В ее основе
лежит замечание, согласно которому
слияние двух отсортированных списков
выполняется быстро. Список из одного
элемента уже отсортирован, поэтому
сортировка слиянием разбивает список
на одноэлементные куски, а затем
постепенно сливает их. Поэтому вся
деятельность заключается в слиянии
двух списков. Сортировку
слиянием можно записать в виде
рекурсивного алгоритма, выполняющего
работу, двигаясь вверх по рекурсии.
•
описание
анализа алгоритма; •
объяснение
принципов выбора подсчитываемых
операций; •
объяснение,
как проводить анализ в наилучшем,
наихудшем и среднем случае; •
работа
с логарифмами, вероятностями и
суммированием; •
описание
функции θ(f),
Ω(f), Ο ( f ) ,
скорости
роста и порядка алгоритма; •
использование
дерева решений для определения нижней
границы сложности; •
приведение
простого рекуррентного соотношения к
замкнутому виду.
Определение
Д.2.1. Трудоемкость
алгоритма. Под
трудоемкостью алгоритма для данной
конкретной проблемы, заданной множеством
D, будем
понимать количество «элементарных»
операций, задаваемых алгоритмом в
принятой модели вычислений. Соответствующую
функцию будем обозначать как fa
(D).
Заметим, что
значением функции трудоемкости является
целое положительное число. При
более детальном анализе ряда алгоритмов
оказывается, что не всегда количество
элементарных операций задаваемых
алгоритмом, т.е. значение fa
(D)
на входе длины
n,
где n
= \D\,
совпадает
с количеством операций на другом входе
такой же длины.
Введем
специальные обозначения, связанные с
граничными значениями функции
трудоемкости данного алгоритма при
фиксированной длине входа. Пусть
DA —
множество допустимых конкретных проблем
данной задачи, решаемой алгоритмом A,
D e DA
— конкретная
проблема (вход алгоритма А),
представляющая
собой упорядоченное множество элементов
(слов) d: В
общем случае существует подмножество
(для большинства алгоритмов собственное)
множества DA,
включающее
все конкретные проблемы, имеющие
размерность n,
— обозначим его через Dn: В
предположении о том, что элементы d,
представляют собой слова фиксированной
длины в алфавите {0,1}, множество Dn„
является конечным — обозначим его
мощность через MDn
, т.е.
MDn
= \Dn\.
Тогда алгоритм
А, имея
на входе различные множества D
e
Dn,
будет,
возможно, задавать в каком-то случае
наибольшее, а в каком-то случае наименьшее
количество операций. Введем
следующие обозначения, отметив, что
соответствующая терминология является
устоявшейся в литературе по анализу
алгоритмов ([2, 3]). 1.
fA(n)
— худший
случай —
наибольшее количество операций,
совершаемых алгоритмом А
для решения
конкретных проблем размерности n: 2.
fA(n)
— лучший
случай —
наименьшее количество операций,
совершаемых алгоритмом А
для решения
конкретных проблем размерности n: 3.
fA(n)
—
средний
случай —
среднее количество операций, совершаемых
алгоритмом А
для решения
конкретных проблем размерности n:
13. Алгоритмы поиска, выборки и сортировки.
12. Основные вопросы анализа алгоритмов. Понятие алгоритмической сложности.
