
- •Содержание
- •Введение.
- •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. Метод ветвей и границ
- •Вопросы к зачету
- •Классификация алгоритмов по виду функции трудоемкости.
- •Приближения в задаче об упаковке рюкзака.
- •Динамическое программирование
- •Метод ветвей и границ. Литература
1.2. Элементарные операции в формальной системе
Решение вопроса о том, что понимать под элементарными операциями в формальной системе, состоит из 2-х шагов:
На первом шаге выбирается значимая операция или группа операций, а на втором – определяется, какие из этих операций содержатся в теле алгоритма, а какие – составляют накладные расходы или уходят на регистрацию и учет данных.
Необходимо уточнить понятие значимой операции, соотнесенное с языком высокого уровня. В качестве таких «элементарных» операций предлагается использовать следующие:
1) Простое присваивание: а ¬ b;
2) Одномерная индексация a[i] : (адрес (a)+i*длина элемента);
3) Арифметические операции: (*, /, -, +);
4) Операции сравнения: a < b;
5) Логические операции (l1) {or, and, not} (l2);
Все операции сравнения считаются эквивалентными (проверка на =, . Арифметические операции разбиваются на две группы: аддитивные и мультипликативные. Аддитивные операции, называемые сложениями, включают сложение, вычитание, увеличение и уменьшение счетчика. Мультипликативные операции (умножение) включают умножение, деление и взятие остатка по модулю.
Разбиение на эти группы связано с тем, что умножения работают дольше, чем сложения. На практике некоторые алгоритмы считаются предпочтительнее других, если в них меньше умножений, даже если число сложений при этом пропорционально возрастает.
За пределами указанных операций остались операции вычисления стандартных функций, которые образуют еще одну даже более времяемкую, чем умножения, группу операций. Обычно их вычисляют с помощью разложения в ряды, и, следовательно, эти вычисления сводимы к указанной группе элементарных операций.
В случае небольшой или простой программы количество выполняемых операций можно посчитать точно.
Пример 1.1. Найти наибольшее значение из 3-х величин (a,b,c)
MaxABC1(a,b,c) max a if b>max then maxb endif if c>max then maxc endif return (max) end |
MaxABC2(a,b,c) if a>b then if a>c then return (a) else return (c) endif else if b>c then return (b) else return (c) endif endif end
|
В данных алгоритмах значимой является операция сравнения. В каждом из них выполняется по 2 сравнения. Первый алгоритм легче прочесть и понять, но с точки зрения выполнения на компьютере у них одинаковый уровень сложности.
С точки зрения времени эти два алгоритма одинаковы, но первый требует больше памяти (переменная max) и, соответственно, дополнительных операций присваивания для определения ее значения. Это дополнительное место не играет роли, если сравниваются числа или символы, но при работе с другими типами данных оно может стать существенным.
Как уже было сказано, в случае небольшой или простой программы количество выполняемых операций как функцию от N можно посчитать точно. Однако в большинстве случаев в этом нет нужды. Разница между алгоритмом, который делает (N+5) операций, и тем, что делает (N+200) операций становится незаметной, как только N становится очень большим.
Пример 1.2. Алгоритм подсчета числа вхождений различных символов в файл.
SumSymb(File)
// инициализация счетчиков
For all 256 символов do
обнулить счетчик
end for
// подсчет вхождений символов
while (в файле еще остались символы) do
ввести очередной символ
увеличить счетчик вхождений символа на 1
end while
end
Этот алгоритм делает 256 проходов в цикле инициализации. Работа с переменной цикла all заключается в ее первоначальной инициализации (присваивании), затем в каждом проходе проверяется, не выходит ли эта переменная за границы цикла, и она получает приращение. То есть в цикле инициализации делается:
257 присваиваний (1 для переменной цикла и 256 для счетчиков);
256 приращений (переменная цикла);
и 257 проверок условия окончания цикла.
То есть всего 770 операций.
Во втором цикле (while) нужно сделать (N+1) раз проверку условия (+1 - для последней проверки, когда файл пуст), и N приращений счетчика.
Таким образом, всего:
присваиваний 257;
приращений 256+N;
проверки условий N+258;
итого (2N+771) операций.
Таким образом, при размере входного файла в 500 символов в алгоритме выполняется 1771 операция, из которых 770 (43%) приходится на инициализацию. Если файл содержит 50 000 символов, то алгоритм сделает 100 771 операцию, из которых 770 связаны с инициализацией (что составляет менее 1% от их общего числа).
Следовательно, вес инициализации по отношению ко времени выполнения алгоритма незначителен. В терминах анализа при увеличении объема входных данных стоимость инициализации становится пренебрежительно малой.