Алгоритм Хопкрофта — различия между версиями
Warrior (обсуждение | вклад) м (→Алгоритм Хопкрофта) |
Warrior (обсуждение | вклад) (→Модификация) |
||
Строка 126: | Строка 126: | ||
Каждая итерация цикла <tex> while </tex> не может быть выполнена быстрее, чем за <tex> O(|Inverse|) </tex> для текущей пары <tex> (C,a)</tex>. Покажем, как достичь этой оценки. | Каждая итерация цикла <tex> while </tex> не может быть выполнена быстрее, чем за <tex> O(|Inverse|) </tex> для текущей пары <tex> (C,a)</tex>. Покажем, как достичь этой оценки. | ||
− | === | + | ===Модификация=== |
+ | Разбиение <tex> P </tex> можно поддерживать четырьмя массивами: | ||
+ | *<tex>Class[r]</tex> {{---}} номер класса, которому принадлежит состояние <tex>r</tex>; | ||
+ | *<tex>Part[i]</tex> {{---}} указатель на голову двусвязного списка, содержащего состояния, принадлежащие классу <tex> i </tex>; | ||
+ | *<tex>Card[i]</tex> {{---}} количество состояний в классе <tex>i</tex>; | ||
+ | *<tex>Place[r]</tex> {{---}} указатель на состояние <tex>r</tex> в списке <tex>Part[Class[r]]</tex>. | ||
+ | |||
+ | Так как мы храним указатель, где находится состояние в двусвязном списке, то операцию перемещения состояния из одного класса в другой можно выполнить за <tex>O(1)</tex>. | ||
+ | |||
+ | Для быстрой проверки, находится ли пара <tex>(C,a)</tex> в очереди <tex>S</tex>, будем использовать массив <tex>InList</tex> размера <tex>|Q| \times |\Sigma|</tex>, где <tex>InList[C][a] = true</tex>, если пара <tex>(C,a)</tex> содержится в очереди. | ||
+ | Так как при разбиении очередного класса <tex>R</tex> на подклассы <tex>R_1</tex> и <tex>R_2</tex> мы в действительности создаем лишь один новый класс, то замена класса <tex>R</tex> в очереди на подклассы, образовавшиеся при разбиении, сводится лишь к взаимодействию с массивом <tex>InList</tex>. В итоге работа с очередью на каждой итерации требует <tex>O(1)</tex> времени. | ||
===Время работы=== | ===Время работы=== |
Версия 12:20, 8 декабря 2013
Пусть дан автомат, распознающий определенный язык. Требуется найти эквивалентный автомат с наименьшим количеством состояний.
Содержание
Минимизация ДКА
Если в ДКА существуют два эквивалентных состояния, то при их объединении мы получим эквивалентный ДКА, так как распознаваемый язык не изменится. Основная идея минимизации состоит в разбиении множества состояний на классы эквивалентности, полученные классы и будут состояниями минимизированного ДКА.
Простой алгоритм
Определение: |
Класс | разбивает класс по символу на и , если
Если класс
может быть разбит по символу , то он содержит хотя бы одну пару неэквивалентных состояний (так как существует строка которая их различает). Если класс нельзя разбить, то он состоит из эквивалентных состояний. Поэтому самый простой алгоритм состоит в том, чтобы разбивать классы текущего разбиения до тех пор пока это возможно.Итеративно строим разбиение множества состояний следующим образом.
- Первоначальное разбиение множества состояний — класс допускающих состояний и класс недопускающих состояний .
- Перебираются символы алфавита , все пары и помещаются в очередь.
- Из очереди извлекается пара , далее именуется как сплиттер.
- Все классы текущего разбиения разбиваются на 2 подкласса (один из которых может быть пустым). Первый состоит из состояний, которые по символу переходят в сплиттер, а второй из всех оставшихся.
- Те классы, которые разбились на два непустых подкласса, заменяются этими подклассами в разбиении, а также добавляются в очередь.
- Пока очередь не пуста, выполняем п.3 – п.5.
Псевдокод
— множество состояний ДКА. — множество терминальных состояний. — очередь пар . — разбиение множества состояний ДКА. — класс состояний ДКА.
for to to while from for in if and in with and for to to
Когда очередь
станет пустой, будет получено разбиение на классы эквивалентности, так как больше ни один класс невозможно разбить.Время работы
Время работы алгоритма оценивается как
, где — количество состояний ДКА, а — алфавит. Это следует из того, что если пара попала в очередь, и класс использовался в качестве сплиттера, то при последующем разбиении этого класса в очередь добавляется два класса и , причем можно гарантировать лишь следующее уменьшение размера: . Каждое состояние изначально принадлежит лишь одному классу в очереди, поэтому каждый переход в автомате будет просмотрен не более, чем раз. Учитывая, что ребер всего , получаем указанную оценку.Алгоритм Хопкрофта
Лемма: |
Класс и , тогда разбиение всех классов (текущее разбиение) по символу любыми двумя классами из эквивалентно разбиению всех классов с помощью по символу . |
Доказательство: |
Разобьем все классы с помощью и по символу , тогда для любого класса из текущего разбиения выполняется
А так как и то выполняется
Из этого следует, что разбиение всех классов с помощью
А так как и то выполняется
|
Алгоритм Хопкрофта отличается от простого тем, что иначе добавляет классы в очередь. Если класс
уже есть в очереди, то согласно лемме можно просто заменить его на и . Если класса нет в очереди, то согласно лемме в очередь можно добавить класс и любой из и , а так как для любого класса из текущего разбиения выполняется- or
то в очередь можно добавить только меньшее из
и .Псевдокод
— множество состояний ДКА. — множество терминальных состояний. — очередь из пар . — разбиение множества состояний ДКА. — класс состояний ДКА.
for to while split by for each in in with and for if in in with and else to
К сожалению, совсем не очевидно, как быстро находить множество
. С другой стороны, понятно, что , где — это множество классов текущего разбиения, из состояний которых в автомате существует переход в состояния сплиттера по символу .Модифицируем наш алгоритм: для каждой очередной пары
будем находить , и с каждым классом состояний из будем производить те же действия, что и раньше.for to while for each in if split by in with and for if in in with and else to
Каждая итерация цикла
не может быть выполнена быстрее, чем за для текущей пары . Покажем, как достичь этой оценки.Модификация
Разбиение
можно поддерживать четырьмя массивами:- — номер класса, которому принадлежит состояние ;
- — указатель на голову двусвязного списка, содержащего состояния, принадлежащие классу ;
- — количество состояний в классе ;
- — указатель на состояние в списке .
Так как мы храним указатель, где находится состояние в двусвязном списке, то операцию перемещения состояния из одного класса в другой можно выполнить за
.Для быстрой проверки, находится ли пара
в очереди , будем использовать массив размера , где , если пара содержится в очереди. Так как при разбиении очередного класса на подклассы и мы в действительности создаем лишь один новый класс, то замена класса в очереди на подклассы, образовавшиеся при разбиении, сводится лишь к взаимодействию с массивом . В итоге работа с очередью на каждой итерации требует времени.Время работы
Время работы модифицированного алгоритма оценивается как
, где — количество состояний ДКА, а — алфавит. В данном случае при последующем разбиении в очередь будет добавлен класс , причем . Каждый переход в автомате будет просмотрен не более, чем раз, ребер всего , отсюда указанная оценка.Литература
- Хопкрофт Д., Мотвани Р., Ульман Д. Введение в теорию автоматов, языков и вычислений, 2-е изд. : Пер. с англ. — М.: Издательский дом «Вильямс», 2002. — С. 177 — ISBN 5-8459-0261-4 (рус.)
- D. Gries. Describing an algorithm by Hopcroft. Technical Report TR-72-151, Cornell University, December 1972.