- •Элементарные алгоритмы для работы с графами
- •Работа алгоритма[править | править вики-текст]
- •Неформальное описание[править | править вики-текст]
- •Формальное описание[править | править вики-текст]
- •Алгоритм поиска в глубину[править | править вики-текст]
- •Нерекурсивные варианты[править | править вики-текст]
- •Общая идея
- •[Править]Пошаговое представление
- •[Править]Реализация
- •[Править]Время работы
- •[Править]Цвета вершин
- •Топологическая сортировка и сильно связные компоненты
- •↑ Пример ориентированного неотсортированного графа, к которому применима топологическая сортировка
- •Пример работы алгоритма[править | править вики-текст]
- •Алгоритмы[править | править вики-текст]
- •Минимальные остовные деревья (мst)
- •Введение
- •Постановка задачи
- •Кратчайшие пути
- •Давайте придумаем что-нибудь простое
- •А если я педант?
- •Задача о максимальном потоке
- •[Править]Определение потока
- •[Править]Пример
- •Увеличивающие пути. Теорема Бержа
- •Алгоритм Эдмондса. Сжатие цветков
- •Эффективная реализация
- •Оптимизация: предварительное построение паросочетания
- •Случай двудольного графа
- •Дальнейшая оптимизация
- •[Править]Реализация
- •[Править]Оценка производительности
- •[Править]Пример несходящегося алгоритма
- •[Править]Оценка быстродействия
- •[Править]Литература
- •Максимальные паросочетания[править | править вики-текст]
- •Описание алгоритма Необходимые определения
- •Теорема Бержа
- •Алгоритм Куна
- •Время работы
- •Реализация
- •Улучшенная реализация
- •[Править]Постановка задачи
- •[Править]Решение
- •[Править]Псевдокод
- •Префиксы и суффиксы строки
- •Прямые переходы
- •[Править]Суффиксные ссылки
- •Основные определения и описание структуры[править | править вики-текст]
- •Свойства суффиксных деревьев[править | править вики-текст]
- •Требования суффиксного дерева к памяти[править | править вики-текст]
- •[Править]Недостатки
- •Метод хеширования
- •[Править]Алгоритм
- •[Править]Псевдокод
- •[Править]Время работы
- •[Править]Надёжность
- •Автоматные модели в программировании
- •[Править]Переходы между состояниями [править]Прямые переходы
- •[Править]Суффиксные ссылки
- •[Править]Псевдокод
- •[Править]Пример использования
- •[Править]Псевдокод
- •[Править]Пример
- •[Править]Время работы
- •[Править]Эффективный алгоритм
- •[Править]Псевдокод
- •[Править]Время работы
- •[Править]Построение префикс-функции по z-функции [править]Постановка задачи
- •[Править]Описание алгоритма
- •Приближенные алгоритмы решения np-трудных задач
[Править]Решение
Для
решения этой задачи заменим исходную
сеть
на
следующим
образом. Сначала добавим в граф вершины
—
исток и
—
сток. Для каждого ребра
добавим
ребра
и
,
а также сделаем в ребре
изменения:
(см.
рисунок 2).
Рисунок 2. Слева - изначальный граф. Для каждого ребра заданы его нижняя и верхняя пропускные способности. Справа - граф после преобразований ребер.
Каждое
ребро изначального графа заменяется
на три новых. Если по ребру
в
исходной сети протекает поток
,
то в новой сети по ребру
должен
течь поток, равный
,
то есть его пропускной способности.
Поток, который вытекает из
по
ребру в
,
заменяется на поток, который протекает
по ребрам
и
(поскольку
сумма их пропускных способностей в
полученном графе равна
).
Аналогично, для вершины
суммарный
входящий поток не изменился. Таким
образом, любой допустимый поток по
любому ребру в изначальном графе можно
распределить между тремя ребрами в
полученном графе. Заметим, что в
сети
все
,
то есть мы имеем обыкновенную сеть.
Требовалось
найти циркуляцию в исходной сети, а
значит проверить существование потока,
для которого выполнено равенство
для
всех вершин графа. Это равносильно
существованию потока между вершинами
и
в
сети
,
который полностью насытит ребра,
исходящие из истока. Действительно,
этот поток в исходном графе насытит
-ое
ребро как минимум на
,
что и является необходимым требованием.
Если этот поток существует, то будет
выполнено:
где
,
то есть для всех исходных вершин;В
,
что удовлетворяет всем ограничениям.
Значит, этот поток и есть циркуляция.
Запустим в новой сети один из алгоритмов поиска максимального потока. Если он не смог полностью насытить все ребра их истока, то и никакой другой по величине поток этого сделать не сможет, значит, циркуляции нет. Для получения величин потоков вдоль каждого ребра в изначальной сети достаточно прибавить к потокам вдоль ребер в сети соответствующие значения минимальной пропускной способности.
[Править]Псевдокод
G // пустой граф, вершины 0 и n + 1 - исток и сток
n, m; // вершин, ребер в исходном графе
edge // ребро с полями (from, to, min_cap, cap)
Для i = 1 to m
считать ребро edge
добавить в граф G ребро (0, edge.to, 0, edge.min_cap)
добавить в граф G ребро (edge.from, edge.to, 0, edge.cap - edge.min_cap)
добавить в граф G ребро (edge.from, n + 1, 0, edge.min_cap)
max_flow = наибольший поток в графе G
Для всех ребер, инцидентных истоку
если для текущего ребра flow < cap
циркуляции НЕТ
Поиск подстрок
Сравнение — «чёрный ящик»
Во всех алгоритмах этого типа сравнение является «чёрным ящиком» для программиста.
Преимущества:
позволяет использовать стандартные функции сравнения участков памяти (man *cmp(3)), которые, зачастую, оптимизированы под конкретное железо.
Недостатки:
не выдается точка, в которой произошло несовпадение.
[править]По порядку сравнения паттерна в тексте
[править]Прямой
Преимущества:
отсутствие регрессии на «плохих» данных.
Недостатки:
не самая хорошая средняя асимптотическая сложность.
[править]Обратный
Паттерн движется по тексту слева направо, но сравнение подстрок происходит справа налево.
Преимущества:
при несовпадении позволяет перемещать паттерн по строке сразу на несколько символов.
Недостатки:
производительность сильно зависит от данных.
[править]Сравнение в необычном порядке
Специфические алгоритмы, основанные, как правило, на некоторых эмпирических наблюдениях над словарём.[1]
[править]По количеству поисковых шаблонов
Сколько поисковых шаблонов может обработать алгоритм за один раз.
один шаблон (англ. single pattern algorithms)
конечное количество шаблонов (англ. finite set of patterns)
бесконечное количество шаблонов (англ. infinite number of patterns) (см. Теория формальных языков)
[править]По необходимости препроцессинга текста
Виды препроцессинга:
Префикс-функция
Z-функция
Бор
Суффиксный массив
Алгоритмы, использующие препроцессинг — одни из самых быстрых в этом классе.
[править]Сравнение алгоритмов
— размер алфавита
—
длина
текста
—
длина
паттерна
—
размер
ответа(кол-во пар)
—
суммарная
длина всех паттернов
Название |
Среднее |
Худшее |
Препроцессинг |
Дополнительная память |
Кол-во поисковых шаблонов |
Порядок сравнения |
Описание |
Наивный алгоритм (Brute Force algorithm) |
|
|
|
|
Single |
Прямой |
Сравнение
— «чёрный ящик». Если
достаточно
мало по сравнению с
,
то асимптотика будет близкой к |
Поиск подстроки в строке с помощью Z-функции |
|
|
|
|
Single |
Прямой |
|
Алгоритм Рабина-Карпа (Karp-Rabin algorithm) |
|
|
|
|
Single / Finite |
Прямой |
Данный алгоритм использует хэширование, что снижает скорость в среднем. Можно модифицировать для поиска нескольких паттернов |
Алгоритм Кнута-Морриса-Пратта (Knuth-Morris-Pratt algorith) |
|
|
|
|
Single |
Прямой |
Использует префикс-функцию |
Алгоритм Колусси (Colussi algorithm) |
|
|
|
|
Single |
Прямой / Обратный |
Оптимизация Алгоритма Кнута-Морриса-Пратта использует как прямой, так и обратный обход |
Алгоритм Ахо-Корасик (Aho–Corasick string matching algorithm) |
|
|
|
|
Finite |
Прямой |
Строит конечный автомат. Можно хранить таблицу переходов как индексный массив (array), а можно как Красно-черное дерево. В последнем случае уменьшится расход памяти, но ухудшится асимптотика |
Алгоритм Shift-Or |
|
|
|
|
Single |
Прямой |
Использует
тот факт, что в современных процессорах
битовые сдвиг и или являются атомарными.
Эффективен, если |
Алгоритм Бойера-Мура (Boyer-Moore algorithm) |
|
|
|
|
Single |
Обратный |
Считается наиболее быстрым из алгоритмов общего назначения. Использует эвристики. Существует большое количество оптимизаций[2] |
Поиск подстроки в строке с помощью суффиксного массива (Suffix array) |
|
|
|
|
Single |
Прямой |
Использует Суффиксный
массив.
Если использовать Largest
common prefix (lcp),
то можно уменьшить асимптотику до |
Поиск подстроки в строке с помощью суффиксного дерева (Suffix tree) |
|
|
|
|
Single |
Прямой |
Позволяет выполнять поиск подстроки в строке за линейное время |
Допустимые/недопустимые сдвиги
Большинство текстовых редакторов умеет искать заданное слово в редактируемом тексте — хочется, чтобы это происходило быстро. Говоря формально, задача поиска подстрок (string-matching problem) состоит в следующем. Пусть даны «текст» — массив T[1..n] длины n и «образец» — массив P[1..m] длины m<n. Мы считаем, что элементы массивов P и T — символы некоторого конечного алфавита (например, X = {0, 1} или X = {а, b, ..., z}). Массивы, состоящие из символов алфавита X, называют строками (strings) символов, или словами в этом алфавите.
Будем говорить, что образец Р входит со сдвигом s, или, эквивалентно, входит с позиции s + 1 в текст Т, если 0≤ s≤ п — m и T[s+1, ... s + m] =P[1...m] (иными словами, если T[s+j] =P[j] при 1 ≤ j≤ m). Если Р входит со сдвигом s в текст Т, то говорят, что s — допустимый сдвиг, в противном случае s — недопустимый сдвиг. Задача поиска подстрок состоит в нахождении всех допустимых сдвигов для данных текста T и образца Р
Пусть требуется найти все вхождения образца P='abaa' в текст T='аbсаbааbс'. Образец входит в текст только один раз, со сдвигом s=3 (стало быть, 3 — допустимый сдвиг).
Первый приходящий в голову алгоритм для поиска образца P в тексте Т последовательно проверяет равенство T[s+1, ... s + m] =P [1...m] для каждого из п — т + 1 возможных значений s.
Можно сказать, что мы двигаем образец вдоль текста и проверяем все его положения . Простейший алгоритм ищет образец P='aab' в тексте Т='acaabc'. Изображены четыре последовательные попытки. Буквы, для которых сравнение прошло успешно, соединены прямыми. Буквы, на которых выявлено несовпадение, соединены ломаными линиями. При этом s=2 - единственный допустимый сдвиг.
Простейший алгоритм — не лучший. Неэффективность простейшего алгоритма связана с тем, что информация о тексте Т, получаемая при проверке данного сдвига s, никак не используется при проверке последующих сдвигов. Между тем такая информация может очень помочь. Пусть, например, P — 'aaab', и мы выяснили, что сдвиг s=0 допустим. Тогда сдвиги 1, 2 и 3 заведомо недопустимы, поскольку T[4] =b.
Конкатенация строк, префикс строки, суффикс строки
В строке важен порядок символов, так, строки 01 и 10 различны. Длина строки равна числу символов в строке. Длина пустой строки равна нулю, длина строки 1 равна единице, длина строки 1001 четырем. Если X и Y строки, то их сцеплением или конкатенацией называется строка XY, полученная приписыванием символов строки Y за символами строки X.
В языке Turbo Pascal определен стандартный тип данных string. Значения этого типа – последовательность символов длиной от 0 до 255. При определении типа строки можно указать в квадратный скобках максимальный размер строки. Если размер строки не указан, то он считается равным 255.
Изображением строки называется последовательность символов, заключенная в апострофы. Значение для строковой переменной можно прочесть из стандартного файла ввода и поместить в стандартный файл вывода.
Операция + используется для конкатенации строк, в этом случае к первой строке приписывается вторая.
К строкам применимы операции отношения. Сравнение осуществляется слева направо в соответствии с кодами символов. При сравнении строк разной длины принято соглашение, что отсутствующий символ меньше любого кода символа строки, поэтому верно ‘дом’ < ‘дома’.
Символы в строке перенумерованы, начиная с 1. Количество символов в строке или длину строки можно определить с помощью стандартной функции length(s). Получить доступ к символу строки можно по его индексу, для этого за строковой переменной в квадратных скобках указывается индекс. Значение строковой переменной изменится, если изменится значение хотя бы одного из символов строки.
