- •Содержание
- •Введение.
- •1. Основы анализа алгоритмов
- •1.1. Сравнительные оценки алгоритмов
- •1.2. Элементарные операции в формальной системе
- •1.3. Классы входных данных
- •1.4. Классификация алгоритмов по виду функции трудоемкости
- •1.5. Классификация скоростей роста. Асимптотический анализ функций
- •1. Оценка (тета)
- •2. Оценка о (о большое)
- •3. Оценка (Омега)
- •1.6. Эффективность рекурсивных алгоритмов
- •1.7. Анализ программ
- •1.8. Вопросы для самоконтроля
- •Классификация алгоритмов по виду функции трудоемкости.
- •2. Алгоритмы поиска и выборки
- •2.1. Последовательный поиск
- •2.2. Двоичный поиск
- •2.3. Задача выборки
- •2.4. Вопросы для самоконтроля
- •3.Алгоритмы сортировки
- •3.1. Сортировка трех чисел по месту
- •3.2. Сортировка вставками
- •3.3. Пузырьковая сортировка
- •3.4. Сортировка Шелла.
- •3.5. Корневая сортировка
- •3.6. Сортировка методом индексов
- •3.7. Быстрая сортировка (алгоритм Хоара)
- •3.8. Вопросы для самоконтроля
- •Сортировка Шелла.
- •Сортировка методом индексов.
- •4. Алгоритмы на графах
- •4.1. Основные понятия теории графов
- •4.2. Структуры данных для представления графов
- •4.3. Алгоритмы обхода вершин графа
- •4.3.1. Обход в глубину
- •4.3.2. Обход в ширину
- •4.4. Поиск остовного дерева минимального веса
- •4.4.1. Алгоритм Дейкстры – Прима
- •4.4.2. Алгоритм Крускала
- •4.5. Алгоритм поиска кратчайшего пути
- •4.6. Вопросы для самоконтроля
- •Структуры данных для представления графов.
- •5. Численные методы
- •5.1. Вычисление значений многочленов
- •5.2. Умножение матриц
- •5.2.1 Стандартный алгоритм умножения матриц
- •5.2.2. Умножение матриц по Винограду
- •5.2.3. Умножение матриц по Штрассену
- •5.3. Вопросы для самоконтроля
- •Стандартный алгоритм умножения матриц.
- •6. Алгоритмы сравнения с образцами
- •6.1. Сравнение строк
- •6.2. Алгоритм Кнута – Морриса – Пратта
- •6.3. Алгоритм Бойера - Мура
- •6.4. Вопросы для самоконтроля
- •7. Вычислительная геометрия
- •7.1. Основные понятия
- •7.2. Векторное произведение векторов
- •7.2.1. Ориентированная площадь треугольника
- •7 .3. Задача о выпуклой оболочке
- •7.3.1. Алгоритм Грэхема
- •7.3.2. Алгоритм Джарвиса
- •7.3.3. Рекурсивный алгоритм
- •7.4. Вопросы для самоконтроля
- •8. Задачи класса np
- •8.1. Примеры np-полных задач
- •8.1.1. Задача о коммивояжере
- •8.1.2. Задача о раскраске графа
- •8.1.3. Раскладка по ящикам
- •8.1.4 Упаковка рюкзака
- •8.1.5. Задача о суммах элементов подмножества
- •8.1.6. Задача о планировании работ
- •8.2. Приближенные эвристические решения nр-полных задач.
- •8.2.1. Жадные приближенные алгоритмы
- •8.2.2. Приближения в задаче коммивояжера
- •8.2.3. Приближения в задаче о раскладке по ящикам
- •8.2.4. Приближения в задаче об упаковке рюкзака
- •8.3. Вопросы для самопроверки
- •Приближения в задаче об упаковке рюкзака.
- •9. Динамическое программирование
- •Часть1--------------------
- •10. Метод ветвей и границ
- •Вопросы к зачету
- •Классификация алгоритмов по виду функции трудоемкости.
- •Приближения в задаче об упаковке рюкзака.
- •Динамическое программирование
- •Метод ветвей и границ. Литература
2.3. Задача выборки
Иногда нам нужен элемент из списка, обладающий некоторыми специальными свойствами, а не имеющий некоторое конкретное значение.
Например, вместо записи с конкретным значением поля нас интересует запись с наибольшим, или наименьшим, или средним значением этого поля.
В общем случае нас может интересовать запись с k-м по величине значением поля.
Один из способов найти такую запись состоит в том, чтобы отсортировать список в порядке убывания по этому полю; тогда запись с k-м по величине значением окажется на k-м месте. На это уйдет гораздо больше сил (и времени), чем необходимо: значения, меньше искомого, нас не интересуют.
Может пригодиться следующий подход: мы находим наибольшее значение в списке и помещаем его в конец. Затем мы можем найти наибольшее значение в оставшейся части списка, исключая уже найденное.
В результате мы получаем второе по величине значение списка, которое можем поместить на второе с конца место в списке. Повторив эту процедуру k раз, мы найдем k-тое по величине значение (мы завершаем сортировку раньше, а, именно, через k шагов)
FindKmax (list, k, N)
// list – список для просмотра
// k – порядковый номер требуемого элемента
// N – размер списка
for i=1 to k do
max list [1]
nom 1 // номер максимального элемента
for j = 2 to N – (i-1) do
if max < list (j) then
max list [j]
nom j
endif
endfor
Swap (list [N – (i – 1)], list [nom])
endfor
return (max)
end
Какова сложность этого алгоритма?
На каждом проходе мы присваиваем переменной max первое значение из списка, и далее сравниваем эту переменную со всеми остальными элементами.
Для первого прохода будет выполнено (N-1) сравнение, для 2-го – (N-2) … для k-того – (N-k), поэтому всего для поиска нам потребуется
fА
(N)
=
Асимптотическая верхняя оценка этой величины при k=N – O(N2).
Замечание. Для значений k > N/2 поиск лучше начинать с конца списка (то есть искать не самое большое, а, наоборот, самое маленькое значение элемента списка).
Для поиска значений из середины списка есть и более эффективный алгоритм. Поскольку нам нужно лишь k-тое по величине значение, то точное местоположение больших (k – 1) элементов нас на самом деле не интересует – нам достаточно знать, что они больше искомого.
Для всякого элемента из списка весь список распадается на 2 части: элементы, большие взятого, и элементы, меньшие взятого.
Если переупорядочить элементы в списке так, чтобы все большие шли за выбранным, а все меньшие – перед ним, и при этом выбранный элемент окажется на p–том месте, то мы будем знать, что он p–ый по значению.
Такое переупорядочивание требует сравнения выбранного элемента со всеми остальными – то есть (N-1) сравнение.
Если нам «повезло», и p = k, то работа закончена.
Если k < p, то нам нужно некоторое большее значение, и проделанную процедуру повторяют со 2-ой частью списка.
Если k > p, то нам нужно меньшее значение, и мы можем воспользоваться первой частью списка; при этом, однако, k следует уменьшить на число откинутых во второй части элементов.
В результате получается следующий рекурсивный алгоритм.
FindKRec (list, start, end, k)
// list – список для просмотра
// start - индекс первого рассматриваемого элемента
// end – индекс последнего рассматриваемого элемента
// k - порядковый номер требуемого по величине элемента
if start < end then
Part (list, start, end, p)
// процедура Part разбивает список на 2 части относительно среднего по
// положению элемента
if p = k then return list [p]
else
if p > k
then return FindKRec (list, start, p-1,k)
else return FindKRec (list, p+1, end, k)
endif
endif
endif
end
Если предположить, что в среднем при разбиении список будет делиться на 2 приблизительно равные части, то ожидаемое количество сравнений:
fА (N) = N + N/2 + N/4 + … О(2N) (не зависит от k)
Рассмотрим работу данного алгоритма на примере списка из N=9 элементов, k=4.
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
номера элементов списка |
||
3 |
7 |
8 |
5 |
2 |
1 |
4 |
6 |
0 |
список list |
||
первое переупорядочивание относительно среднего элемента (2) |
|||||||||||
1 |
0 |
2 |
6 |
4 |
5 |
8 |
7 |
3 |
р=3, kр, k>p |
8 сравнений |
|
работаем со второй частью списка, ее середина – 5; первая часть остается неизменной |
|||||||||||
1 |
0 |
2 |
4 |
3 |
5 |
7 |
8 |
6 |
р=6, kр, k<p |
5 сравнений |
|
работаем с первой частью списка, ее середина – 4; вторая часть – без изменений |
|||||||||||
1 |
0 |
2 |
3 |
4 |
5 |
7 |
8 |
6 |
р=5, kр, k<p |
1 сравнение |
|
1 |
0 |
2 |
3 |
4 |
5 |
7 |
8 |
6 |
р=4, k=р, значение найдено |
1 сравнение |
|
|
ИТОГО 15 сравнений |
||||||||||
