ОСНОВЫ ПРОГРАММНОГО КОНСТРУИРОВАНИЯ я Ка ч ем Вр ес тво Лекция № 11 28 апреля 2015 г. Стоимость https://github.com/cypok/console_graphics https://github.com/cypok/sdl_example ЗАДАЧКА О СОРТИРОВКАХ Даны 7 массивов чисел: 1. B A 9 6 1 D F 3 0 E 8 5 C 2 4 7; 2. 1 6 9 3 0 A 8 5 B 2 4 7 C D E F; 3. 0 1 2 3 4 5 F 6 B E 8 D C 9 A 7; 4. C A B 7 8 5 9 6 0 1 3 2 4 D E F; 5. 4 2 0 3 1 5 7 6 9 A 8 B C E F D; 6. 1 6 9 A B D F 3 0 E 8 5 C 2 4 7; 7. 0 1 2 3 4 5 6 7 8 9 A B C D E F. Массив 1 — начальный. Массив 7 — отсортированный. Массивы 2-6 являются промежуточными состояниями при сортировке начального массива разными алгоритмами: a. сортировка выбором, b. пузырьковая сортировка (обычная, слева направо), c. сортировка вставками, d. быстрая сортировка (без перемешивания, первый элемент как медиана), e. пирамидальная сортировка. Определить, какой массив 2-6 соответствует какой сортировке a-e. Ответа недостаточно, нужно объяснение, почему именно этот массив может соответствовать именно этой сортировке. Подсказка: нужно заметить характерные свойства каждой из сортировок. СОРТИРОВКА СЛИЯНИЕМ (MERGESORT) • Разделение. Как-либо делим массив на 2 части. • Покорение. Для каждой из частей размером больше 1 алгоритм вызывается рекурсивно. • Комбинирование. Линейное слияние отсортированных подмассивов. СОРТИРОВКА СЛИЯНИЕМ (MERGESORT) Линейный алгоритм слияния 2 отсортированных массивов: • Заводим 2 индекса, изначально указывающих на начала массивов. • На каждом шаге выбираем наименьший элемент из двух, на которые указывают индексы, записываем его в результирующий массив и сдвигаем этот индекс. Если соответствующий массив кончился, отбрасываем индекс вместе с массивом. • Когда все массивы кончились, в результирующем массиве лежат все элементы, отсортированные. ОСОБЕННОСТИ СОРТИРОВКИ СЛИЯНИЕМ • Сложность O(N log(N)). дополнительная память (еще один массив размером N). • Требуется • Осуществляет доступ к сортируемым элементам последовательно. Поэтому годится для сортировки связанных списков. • Является стабильной, в отличие от быстрой и пирамидальной сортировок. АНИМАЦИЯ http://www.sorting-algorithms.com/merge-sort ИНТЕРВЬЮ БАРАКА ОБАМЫ В GOOGLE Eric Schmidt: What is the most efficient way to sort a million 32-bit integers? Barack Obama: Well... Eric Schmidt: Maybe–I'm sorry... Barack Obama: No, no, no, no. I think–I think the bubble sort would be the wrong way to go. СОРТИРОВКА ЗА ЛИНЕЙНОЕ ВРЕМЯ произвольных ключей математически доказано: количество сравнений не менее N log(N). • Для •А что, если ключи не произвольные? • Сколько операций нужно, чтобы отсортировать последовательность битов? 01001010100000110010111001001111 СОРТИРОВКА ПОДСЧЕТОМ (COUNTING SORT) • Предположим, что все ключи – целые числа 0…K−1. массив счетчиков C[k] размером K, изначально заполненный нулями. • Заведем • Подсчитаем количество вхождений каждого ключа. к каждому элементу C[k] все предыдущие, получая: C[k] = количество ключей, не превышающих k. • Добавим с конца массива элементов, ставя элемент A[n] на позицию C[Key(A[n])]−1 и уменьшая счетчик. • Идем СВОЙСТВА СОРТИРОВКИ ПОДСЧЕТОМ • Временная сложность O(N+K). • Стабильность. • Дополнительная память: массив длины N (если стабильность не нужна, можно обойтись без него); • вспомогательный • массив счетчиков. ПОРАЗРЯДНАЯ СОРТИРОВКА (RADIX SORT) • Предположим, что • Алгоритм «от младшей к старшей»: • Стабильная •… ключи состоят из d «цифр». сортировка массива по 1-й цифре (младшей). по 2-й цифре. •… •… по d-й цифре (старшей). • Годится для сортировки по составным ключам. • Сложность O(N). КАРМАННАЯ СОРТИРОВКА ключи – N равномерно распределенных в интервале [0,1) чисел. • Предположим, что • Поделим интервал на N подинтервалов (карманы). • Раскидаем элементы по карманам. • Отсортируем элементы в каждом кармане отдельно (сортировка вставками). • Последовательно •В выложим содержимое карманов. среднем работает за O(N). ЗАДАЧА ПОИСКА ЛИНЕЙНЫЙ ПОИСК Дан массив A[0…N−1] и ключ K. Требуется определить положение элемента с данным ключом в массиве или установить, что его там нет. Последовательно сравниваем ключи, пока не найдем совпадающий ключ или массив не кончится. Сложность O(N). БИНАРНЫЙ ПОИСК (ДЕЛЕНИЕМ ПОПОЛАМ) Дан отсортированный массив A[0…N−1] и ключ K. Требуется определить положение элемента с данным ключом в массиве или установить, что его там нет. Возьмем серединный элемент массива (m-й) и сравним его ключ Km с K: • Если Km = K, то ключ найден. • Если Km < K, то продолжаем поиск в правой половине: A[m+1…N−1]. • Если Km > K, то продолжаем поиск в левой половине: A[0…m−1]. Сложность O(log(N)). ЗНАКОМЫЕ СТРУКТУРЫ ДАННЫХ ДЛЯ ПОИСКА • Двоичные деревья поиска. • Хеш-таблицы. ПОИСК ПОДСТРОКИ В СТРОКЕ строка — «стог сена» длины N («abcddsdfff»). Определить, встречается ли в ней строка-«иголка» длины M («ddf») и если да, то на какой позиции. • Задача: дана • Метод грубой силы: подставляем «иголку» к каждой возможной позиции в «стоге сена», покуда не совпадет или «стог» не кончится. Количество сравнений: O((N−M)·M). КАК УБЫСТРИТЬ ПОИСК? «стог сена» «иголка» b c a b c b c b a a … a a a a a • Явно нет смысла сдвигать «иголку» на одну позицию вправо. • Хорошо бы как-то учесть частичное совпадение строк и не проверять все символы заново каждый раз. АЛГОРИТМ БОУЭРА-МУРА (МОДИФИЦИРОВАННЫЙ) • Строки h и n: «стог сена» (haystack) и «иголка» (needle), соответственно. • Хитрый массив d, индексируемый символами x из алфавита. Для каждого символа x из h: • если x отсутствует в n, то d[x] равно M; • если x – не последний в n, то d[x] равно расстоянию от самого правого вхождения x в n до последнего символа в n; • если x – последний в n, то d[x] равно расстоянию от предпоследнего вхождения x в n до конца n. ПОИСК ПО БОУЭРУ-МУРУ • Подставляем «иголку» к началу «стога» (i = M−1). символы с конца «иголки»: h[k] и n[j], уменьшая k и j (изначально k = i, j=M−1). • Сравниваем • Если j дошло до начала «иголки» (0), подстрока найдена! на каком-то шаге h[k] ≠ n[j], то сдвигаем «иголку» на d[h[i]] вправо (i += d[h[i]]). • Если АЛГОРИТМ РАБИНА-КАРПА • Представим, что «иголка» и «стог» состоят только из символов 0…9. Пример: ищем «123» в «8786124612612341». • Tn = «иголка» как число в десятичной записи. • T[k] = M символов «стога», начиная с позиции k, как число. • T[k+1] = (T[k] − 10M−1·h[k])·10 + h[k+M]. • Линейное время поиска. РАБИН-КАРП В РЕАЛЬНОЙ СИТУАЦИИ • Вместо 10 взять 256 (для ASCII-строк) и выполнять вычисления по модулю некоего большого числа. 10 взять 2, за большое число принять 2N, где N – размер слова. • Вместо • Что-то • При наподобие хеш-функции. совпадении хешей посимвольная проверка. КОНЕЦ ОДИННАДЦАТОЙ ЛЕКЦИИ Долго искать в темной комнате черную кошку, особенно если ее там нет.