Сложность, сортировка, поиск Работа по дисциплине «Алгоритмы и структуры данных» Выполнена Садукевич А.В., 271ПИ 1 Введение Теория сложности вычислений возникла из потребности сравнивать быстродействие алгоритмов (например, алгоритмов поиска и сортировки), чётко описывать их поведение (время исполнения и объём необходимой памяти) в зависимости от размера входа. Сложность - мера использования алгоритмом ресурсов времени или пространства. Время выполнения алгоритма определяется количеством тривиальных шагов, необходимых для решения проблемы и зависит от размера входных данных (далее n) Пространство объёмом памяти или места на носителе данных, используемом алгоритмом. 3 Сложность F (n) – функция трудоемкости, определяющая зависимость между входными данными и кол-вом базовых операций ( временными затратами алгоритма ) Асимптотическая оценка O(g(n)) = { F(n): существуют положительные константы c и n0 такие, что 0≤ f(n) ≤ cg(n) для всех n≥ n0} 4 Классы оценок сложности - множества вычислительных проблем, для решения которых известны алгоритмы, схожие по сложности O(1) – постоянное время O(log(n)) – логарифмическое время O(n) – линейное время O(n log(n)) – "n-log-n" время O(n2) – квадратичное время O(n3) – кубическое время O(2n) – экспоненциальное время 5 Класс P Проблемы, решение которых считается «быстрым», то есть полиноминально зависящим от размера входных данных (например, O(n), O(n2)) Примеры Сортировка Поиск во множестве Выяснение связности графов 6 Класс NP Проблемы, для решения которых используются лишь алгоритмы, экспоненциально зависящие от размера входных данных (например, O(2n)) Примеры Задача коммивояжера Задача выполнимости булевых формул факторизация 7 Время выполнения алгоритма для небольших n 8 Время выполнения алгоритма для больших n 9 Алгоритм поиска - алгоритм нахождения заданного значения произвольной функции в некотором наборе значений Виды поиска Линейный поиск (последовательный поиск, поиск за линейное время) Двоичный (бинарный) поиск 10 Линейный (последовательный) поиск - простейший вид поиска заданного элемента на некотором отрезке (множестве), осуществляемый путем последовательного сравнения очередного рассматриваемого значения с искомым до тех пор, пока эти значения не совпадут 11 Время выполнения Если отрезок имеет длину n, то найти решение с точностью до ε можно за время n/ ε Сложность Сложность линейного поиска O(n) 12 Пример реализации алгоритма линейного поиска на языке C++ template <class T> int linear_search(const vector<T>& v, const T& item) { for (int i = 0; i < v.size(); i++) { if (v[i] == item) { return i; // элемент найден } } return -1; // элемент не найден } 13 За: Не требует сортировки значений множества Не требует дополнительного анализа функции. Не требует дополнительной памяти. => Следовательно, может работать в потоковом режиме при непосредственном получении данных из любого источника. Против: Малоэффективен по сравнению с другими алгоритмами поиска. =>Следовательно, используется, если множество содержит небольшое количество элементов 14 Двоичный (бинарный) поиск - поиск заданного элемента на упорядоченном множестве, осуществляемый путем неоднократного деления этого множества на две части таким образом, что искомый элемент попадает в одну из этих частей. Поиск заканчивается при совпадении искомого элемента с элементом- границей между частями множества или при отсутствии искомого элемента. 15 Описание метода бинарного поиска 1. Упорядоченное по возрастанию множество элементов, необходимо найти элемент с значением, равным 9 2. Выбор середины вектора – элементаграницы 16 Описание метода бинарного поиска 3. Сравнение элемента-границы с искомым элементом: 9 < 21, отбрасываем правую часть 4. В левой части повторяем алгоритм до тех пор, пока элемент-граница не равен 9 17 Пример реализации алгоритма бинарного поиска на языке C++ template <class T> int binary_search(const vector<T>& v, const T& item) { int low = 0; int high = v.size() - 1; int mid; while (low <= high) { // нахождение элемента-границы mid = (low + high) / 2; If (v[mid] < item) { low = mid + 1;} // обращаемся выше элемента-границы else if (v[mid] > item) { high = mid - 1; // обращаемся ниже элемента-границы }else { //элемент найден return mid;}} return -1; // элемент не найден } 18 Время выполнения Когда функция имеет вещественный аргумент, найти решение с точностью до ε можно за время (log a), где a=1/ε. Когда аргумент дискретен, и изначально лежит на отрезке длины n, поиск решения займёт (1+ log n) времени Сложность Сложность бинарного поиска O( log n) 19 За: Относительная быстрота выполнения поиска (по линейным) Против: Бинарный поиск может применяться только на упорядоченном множестве 20 Алгоритм сортировки - алгоритм для упорядочения элементов в списке. В случае, когда элемент списка имеет несколько полей, поле, служащее критерием порядка, называется ключом сортировки. Остальные поля могут в ней не участвовать. 21 Характеристики алгоритмов сортировки Устойчивость – изменение относительного положения равных элементов Естественность поведения – улучшение работы алгоритма при улучшении (частичной или полной сортировке) входных данных Сравнения - количество операций сравнения элементов Перестановки - количество перестановок элементов 22 Алгоритм сортировки подсчётом - алгоритм сортировки массива целых положительных чисел, лежащих в определённом диапазоне. При выполнении этого алгоритма подсчитывается число элементов, меньших текущего элемента х, и число одинаковых элементов, а затем выстраивается новый массив, в который элемент х записывается сразу «на свое место» (исходя из этих подсчётов) 23 Схема реализации сортировки подсчётом // A[1..n] – входной массив, B[1..n] – выходной, C[1..k] // вспомогательный, k- максимальное число элементов в массиве A for i := 1 to k do C[i] := 0 for j :=1 to length[A] do C[A[j]] := C[A[j]]+ 1 C[i] равно количеству элементов, равных i for i := 2 to k do C[i] := C[i] + C[i -1] C[i] равно количеству элементов, не превосходящих i for j := length[A] downto 1 do B[C[A[j]]] := A[j] C[A[j]] := C[A[j]]-1 24 Сложность Циклы в строках 1-2 и 6-7 работают за время O(k), Циклы в строках 3-4 и 10-11 - за время O(n), Значит, весь алгоритм работает за время O(n+k); если k= O(n), то время работы (временная сложность) есть O(n) 25 За: Устойчивая сортировка: если во входном массиве присутствуют несколько одинаковых чисел, то в выходном массиве они стоят в том же порядке, что и во входном Против: Ограничения, налагаемые на входные данные (массив целых положительных чисел в известном диапазоне) 26 Алгоритм сортировки выборкой - неустойчивый алгоритм сортировки, при котором выбирается минимальное значение, производится обмен этого значения со значением на первой позиции в списке (массиве, множестве). Эти операции повторяются с хвостом списка: исключаются уже отсортированные элементы – до тех пор, пока весь список не будет отсортирован. 27 Иллюстрация выполнения алгоритма сортировки выборкой 1.Начальный массив 2.Минимальный элемент = 12 3. Минимальный элемент = 7 4…. Минимальный элемент = 3 Затем повторяются те же шаги без учёта отсортированного элемента 5. Итог – отсортированный массив 28 Пример реализации алгоритма сортировки выборкой на языке C++ template <class T> void selection_sort(vector<T>& v) { for (int i = 0; i < v.size() - 1; i++) { int best = i; for (int j = i + 1; j < v.size(); j++) { if (v[j] < v[best]) { best = j; } } if (best != i) { T temp = v[i]; v[i] = v[best]; v[best] = temp; } } } 29 Сложность алгоритма сортировки выборкой На массиве из n элементов имеет время выполнения в худшем, среднем и лучшем случае O(n2), предполагая что сравнения делаются за постоянное время. 30 Алгоритм быстрой сортировки - алгоритм сортировки списка (множества, массива), основанный на принципе «разделяй и властвуй», самый быстрый из известных универсальных алгоритмов сортировки. Алгоритм быстрой сортировки Выбираем в массиве некоторый элемент, который будем называть опорным элементом; Разделяем массив таким образом, чтобы все элементы, меньшие или равные опорному элементу, оказались слева от него, а все элементы, большие опорного — справа от него; Рекурсивно упорядочиваем подмассивы, лежащие слева и справа от опорного элемента; Базой рекурсии являются наборы, состоящие из одного или двух элементов. Первый возвращается в исходном виде, во втором, при необходимости, сортировка сводится к перестановке двух элементов. Сложность алгоритма быстрой сортировки Лучший случай: O (n log n); Промежуточный случай: O (n log n); Худший случай: O (n2); Достоинства: Самый быстродействующий из всех существующих неспециализированных алгоритмов обменной сортировки; Простая реализация; Хорошо сочетается с алгоритмами кэширования и подкачки памяти; Хорошо работает на «почти отсортированных» (естественность поведения) Недостатки: При классической реализации требует в худшем случае много дополнительной памяти; Неустойчив — если требуется устойчивость, приходится расширять ключ. Спасибо за внимание! Москва, 2008 35