- •Содержание
- •Введение.
- •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. Метод ветвей и границ
- •Вопросы к зачету
- •Классификация алгоритмов по виду функции трудоемкости.
- •Приближения в задаче об упаковке рюкзака.
- •Динамическое программирование
- •Метод ветвей и границ. Литература
3.4. Сортировка Шелла.
Этот метод был предложен Шеллом (Дональд Л. Шелл) в 1959 г. Основная идея этого алгоритма заключается в том, чтобы устранить массовый беспорядок в массиве, сравнивая элементы, далеко отстоящие друг от друга.
Необычность метода состоит в том, что он рассматривает весь список как совокупность перемешанных подсписков. На первом шаге эти подсписки представляют собой просто пары элементов. На втором шаге - в каждой группе по 4 элемента. На каждом следующем шаге число элементов в каждом подсписке увеличивается, а число подсписков соответственно уменьшается.
Сортировка подсписков выполняется путем однократного применения сортировки вставками. Но примем во внимание, что в сортировке Шелла упорядочиваются перемешанные подсписки, и обмен элементов может уменьшить не одну инверсию, а большее их число.
ShellSort (A,N)
pass [ log 2 N] // число шагов алгоритма
while pass 1 do
// inc - величина шага между элементами подсписка
// Значение шага inc так же равно числу подсписков.
inc 2 ^ pass – 1
for start = 1 to inc do
InsertSort (A, N, start, inc)
endfor
pass pass - 1
endwhile
end
Было доказано, что сложность этого алгоритма O(N3/2).
Для этой сортировки чрезвычайно важна последовательность шагов. Выбор последовательности шагов может оказать решающее влияние на сложность сортировки, однако попытки поиска оптимальной последовательности не привели к искомому результату.
Пример 3.3 Сортировка Шелла для списка (7, 3, 9, 4, 2, 5, 6, 1, 8).
1 шаг pass = [log 2 9] = 3, inc = 2^3-1=7. Выделим 7 подсписков. В первый подсписок включаются первый и восьмой (1+inc) элементы, во второй – второй и девятый, в третий – только 3 элемент соответственно и т.д. (7 1) (3 8) (9) (4) (2) (5) (6)
Инверсии (нарушение порядка) обнаружены в первом подсписке. Все подсписки сортируются методом вставок (поменяв местами 7 и 1 мы убираем семь инверсий в обычной сортировке). Сравнений 2, перестановок (сдвигов) 1. После сортировки список имеет вид:
1, 3, 9, 4, 2, 5, 6, 7, 8.
2 шаг. Pass=2, inc=3. Выделяем 3 подсписка: (1 4 6) (3 2 7) (9 5 8). Сортируем подсписки. Сравнений 7, перестановок 3. Итоговый список:
1, 2, 5, 4, 3, 8, 6, 7, 9
3 шаг. Pass=1, inc=1. Работаем с общим списком. Обычная сортировка методом вставок. Сравнений 13, перестановок – 5.
Общее число сравнений – 22, перестановок – 9 (против 26 и 19 соответственно в обычной сортировке вставками для этого случая).
3.5. Корневая сортировка
Все элементы списка –целые, положительные, несовпадающие числа. При корневой сортировке упорядочивание списка происходит без непосредственного сравнения ключевых значений между собой.
При этом создается набор «стопок» (пачек), а элементы распределяются по стопкам в зависимости от значений ключей. Собрав значения обратно и повторив процедуру для последовательных частей ключа, мы получаем отсортированный список.
Чтобы такая процедура работала, распределение по стопкам и последующую сборку необходимо выполнять очень аккуратно.
Рассмотрим этот алгоритм на примере.
Пример 3.4 Дан список из 16 элементов
310 213 023 130 013 222 301 032 201 111 323 002
330 102 231 120
1 проход. Делим список на стопки по значению младшего разряда.
N стопки Содержание
0 310 130 330 120
1 301 201 111 231
2 222 032 002 102
3 213 023 013 323
Объединяем стопки в единый список
310 130 330 120 301 201 111 231 222 032 002 102
213 023 013 323
2 проход. Делим список на стопки по значению следующего разряда (десятки).
0 301 201 002 102
1 310 111 213 013
2 120 222 023 323
3 130 330 231 032
Объединяем стопки в единый список
301 201 002 102 310 111 213 013 120 222 023 323
130 330 231 032
3 проход. Последнее деление по значению старшего разряда (сотни).
0 002 013 023 032
1 102 111 120 130
2 201 213 222 231
3 301 310 323 330
Окончательный вариант списка
002 013 023 032 102 111 120 130 201 213 222 231
301 310 323 330
Ниже приведен возможный вариант алгоритма для ключа, в котором используется 10 цифр (следовательно, раскладка возможна по 10 стопкам), размер ключа КeySize определяет количество разрядов в элементе списка.
RadixSort (A, N)
// А – сортируемый список
// N – размер списка
shift 1
for i=1 to КeySize do
for j=1 to N do
st_nom (A[j]/Shift) mod 10
//процедура Append записывает элемент в соответствующую стопку
Append (st[st_nom], A[j])
endfor
// процедура Combine объединяет стопки в общий список
Combine(St, А)
shift shift * 10
endfor
end
При вычислении значения переменной st_nom (номер стопки) из ключа вытаскивается одна цифра. При делении на shift ключевое значение сдвигается на несколько позиций вправо, а последующее применение операции mod оставляет лишь цифру единиц полученного числа.
При первом проходе с величиной сдвига 1 деление не меняет числа, а результатом операции mod служит цифра единиц ключа. При втором проходе с величиной сдвига shift=10, поэтому целочисленное деление и последующее применение операции mod дают цифру десятков.
Процедура Combine комбинирует стопки и формирует их все в один список. Этот переформированный список служит основой для следующего прохода
Анализ алгоритма.
Каждый ключ просматривается по одному разу на каждый разряд, присутствующий в самом длинном ключе. Поэтому, если в самом длинном ключе М цифр, а число ключей равно N, то сложность корневой сортировки O(MN).
Обычно длина ключа невелика по сравнению с числом ключей (например, при ключе из 6 цифр число записей может быть миллион: от 000000 до 999999).
Доказано, что длина ключа не играет роли, и алгоритм имеет линейную сложность O(N). Это очень эффективная сортировка, поэтому возникает вопрос, зачем вообще нужен какие-либо другие методы?
Ключевым препятствием в реализации корневой сортировки служит ее неэффективность по памяти. В предыдущих алгоритмах дополнительная память равняется длине записи (элемента). Теперь дополнительной памяти требуется гораздо больше.
Если стопки реализовать массивами, то эти массивы должны быть чрезвычайно велики (на самом деле, величина каждого из них должна совпадать с длиной списка, поскольку у нас нет оснований предполагать, что значения ключей распределены равномерно по стопкам).
Вероятность того, что во всех стопках окажется одинаковое число записей совпадает с вероятностью того, что все записи попадут в одну стопку. И то, и другое может произойти.
Поэтому реализация на массивах приведет дополнительно к выделению 10N записей для числовых ключей, ЗЗN (если русские буквы) для символьных ключей (причем это место еще увеличивается, если в ключ могут входить произвольные символы или в буквенных ключах учитывается регистр).
Кроме того, при использовании массивов нам потребуется время на копирование записей в стопку на этапе формирования стопок и на обратное их копирование в список на этапе сборки списка (то есть, каждая запись перемещается 2М раз).
Таким образом, повышение временной эффективности достигается за счет увеличения дополнительной памяти и решение о выборе данного алгоритма должно приниматься на основе комплексной оценки.
Другой подход состоит в объединении записей в список со ссылками. В этом случае, как помещение записи в стопку, так и ее возвращение в список требует лишь изменения ссылок. Так как на ссылку уходит от 2-х до 4-х байт, дополнительная память все равно значительна – 2N или 4N байт.