Редактирование: Минимизация ДКА, алгоритм Хопкрофта (сложность O(n log n))

Перейти к: навигация, поиск

Внимание! Вы не авторизовались на сайте. Ваш IP-адрес будет публично видимым, если вы будете вносить любые правки. Если вы войдёте или создадите учётную запись, правки вместо этого будут связаны с вашим именем пользователя, а также у вас появятся другие преимущества.

Правка может быть отменена. Пожалуйста, просмотрите сравнение версий, чтобы убедиться, что это именно те изменения, которые вас интересуют, и нажмите «Записать страницу», чтобы изменения вступили в силу.
Текущая версия Ваш текст
Строка 18: Строка 18:
 
# Перебираются символы алфавита <tex>c \in \Sigma</tex>, все пары <tex>\langle F,\ c \rangle</tex> и <tex>\langle Q \setminus F, c \rangle</tex> помещаются в очередь.
 
# Перебираются символы алфавита <tex>c \in \Sigma</tex>, все пары <tex>\langle F,\ c \rangle</tex> и <tex>\langle Q \setminus F, c \rangle</tex> помещаются в очередь.
 
# Из очереди извлекается пара <tex>\langle C,\ a \rangle</tex>, <tex>C</tex> далее именуется как сплиттер.
 
# Из очереди извлекается пара <tex>\langle C,\ a \rangle</tex>, <tex>C</tex> далее именуется как сплиттер.
# Каждый класс <tex>R</tex> текущего разбиения разбиваются на 2 подкласса (один из которых может быть пустым). Первый состоит из состояний, которые по символу <tex>a</tex> переходят в сплиттер <tex>(R_1)</tex>, а второй из всех оставшихся <tex>(R_2)</tex>.  
+
# Каждый класс <tex>R</tex> текущего разбиения разбиваются на 2 подкласса (один из которых может быть пустым). Первый состоит из состояний, которые по символу <tex>a</tex> переходят в сплиттер (<tex>R_1</tex>), а второй из всех оставшихся (<tex>R_2</tex>).  
# Если <tex>R</tex> разбился на два непустых подкласса (то есть <tex> R_1 \ne \emptyset \ \land \  R_2 \ne \emptyset </tex>).
+
# Если <tex>R</tex> разбился на два непустых подкласса (т.е. <tex> R_1 \ne \emptyset \ \land \  R_2 \ne \emptyset </tex>).
 
## В разбиении <tex>P</tex> класс <tex>R</tex> заменяется на свои подклассы <tex>R_1</tex> и <tex>R_2</tex>.
 
## В разбиении <tex>P</tex> класс <tex>R</tex> заменяется на свои подклассы <tex>R_1</tex> и <tex>R_2</tex>.
 
## Перебираются символы алфавита <tex>c \in \Sigma</tex>, все пары <tex>\langle R_1, c \rangle</tex> и <tex>\langle R_2, c \rangle</tex> помещаются в очередь.
 
## Перебираются символы алфавита <tex>c \in \Sigma</tex>, все пары <tex>\langle R_1, c \rangle</tex> и <tex>\langle R_2, c \rangle</tex> помещаются в очередь.
Строка 32: Строка 32:
 
*<tex>\mathtt{R}</tex> {{---}} класс состояний ДКА.
 
*<tex>\mathtt{R}</tex> {{---}} класс состояний ДКА.
  
   '''function''' findEquivalenceClasses<tex>(Q,\ F,\ \delta)</tex>: '''vector'''
+
   '''pair<set, set>''' '''function''' findEquivalenceClasses<tex>(Q,\ F,\ \delta)</tex>:
 
     <tex>\mathtt{P} \leftarrow \{ F,\ Q \setminus F \}</tex>
 
     <tex>\mathtt{P} \leftarrow \{ F,\ Q \setminus F \}</tex>
 
     <tex>\mathtt{S} \leftarrow \varnothing </tex>
 
     <tex>\mathtt{S} \leftarrow \varnothing </tex>
Строка 94: Строка 94:
 
*<tex>\mathtt{R}</tex> {{---}} класс состояний ДКА.
 
*<tex>\mathtt{R}</tex> {{---}} класс состояний ДКА.
  
   '''function''' findEquivalenceClasses<tex>(Q,\ F,\ \delta)</tex>: '''vector'''
+
   <tex>\mathtt{findEquivalenceClasses}(Q,\ F,\ \delta)</tex>:
 
     <tex>\mathtt{P} \leftarrow \{ F, \ Q \setminus F \}</tex>
 
     <tex>\mathtt{P} \leftarrow \{ F, \ Q \setminus F \}</tex>
 
     <tex>\mathtt{S} \leftarrow \varnothing </tex>
 
     <tex>\mathtt{S} \leftarrow \varnothing </tex>
 
     '''for''' <tex>c \in \Sigma</tex>
 
     '''for''' <tex>c \in \Sigma</tex>
       push <tex>\langle F,\ c \rangle</tex>, <tex>\langle Q \setminus F,\ c \rangle</tex> '''into''' <tex> \mathtt{S}</tex>
+
       '''push''' <tex>\langle F,\ c \rangle</tex>, <tex>\langle Q \setminus F,\ c \rangle</tex> '''into''' <tex> \mathtt{S}</tex>
 
     '''while''' <tex>\mathtt{S} \ne \varnothing</tex>
 
     '''while''' <tex>\mathtt{S} \ne \varnothing</tex>
       <tex>\langle C,\ a \rangle</tex> <tex>\leftarrow</tex> pop '''from''' <tex>\mathtt{S}</tex>
+
       <tex>\langle C,\ a \rangle</tex> <tex>\leftarrow</tex> '''pop''' '''from''' <tex>\mathtt{S}</tex>
 
       '''for''' <tex>R</tex> '''in''' <tex>\mathtt{P}</tex>  
 
       '''for''' <tex>R</tex> '''in''' <tex>\mathtt{P}</tex>  
 
         <tex> R_1, R_2 \leftarrow </tex> <tex>\mathtt{split}(R,\ C,\ a)</tex>
 
         <tex> R_1, R_2 \leftarrow </tex> <tex>\mathtt{split}(R,\ C,\ a)</tex>
         '''if''' <tex> R_1 \ne \varnothing </tex> '''and''' <tex> R_2 \ne \varnothing </tex>  
+
         '''if''' <tex> R_1 \ne \varnothing </tex> '''and''' <tex> R_2 \ne \varnothing </tex>
         replace <tex>R</tex> '''in''' <tex>\mathtt{P}</tex> with' <tex>R_1</tex> '''and''' <tex>R_2</tex>
+
         '''replace''' <tex>R</tex> '''in''' <tex>\mathtt{P}</tex> '''with''' <tex>R_1</tex> '''and''' <tex>R_2</tex>
         '''if''' <tex>\langle R,\ c \rangle</tex> '''in''' <tex> \mathtt{S}</tex> <font color=darkgreen>// смотрим, есть ли пара <tex>\langle R,\ c \rangle</tex> в очереди </font>
+
         '''if''' <tex>\langle R,\ c \rangle</tex> '''in''' <tex> \mathtt{S}</tex>
           remove <tex>\langle R, c \rangle</tex> '''from''' <tex>\mathtt{S}</tex> <font color=darkgreen>// заменяем её на пары <tex>\langle R_1, c \rangle</tex>, <tex>\langle R_2, c \rangle</tex> если пара есть </font>
+
           '''remove''' <tex>\langle R, c \rangle</tex> '''from''' <tex>\mathtt{S}</tex>
           push <tex>\langle R_1, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
+
           '''push''' <tex>\langle R_1, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
           push <tex>\langle R_2, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
+
           '''push''' <tex>\langle R_2, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
 
         '''else'''
 
         '''else'''
             '''if''' <tex> |\mathtt{P}[R_1]| \leqslant |\mathtt{P}[R_2]| </tex> <font color=darkgreen>// вставляем любую иначе</font>
+
             '''if''' <tex> |\mathtt{P}[R_1]| \leqslant |\mathtt{P}[R_2]| </tex>
               push <tex>\langle R_1, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
+
               '''push''' <tex>\langle R_1, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
 
             '''else'''
 
             '''else'''
               push <tex>\langle R_2, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
+
               '''push''' <tex>\langle R_2, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
 
     '''return''' <tex>\mathtt{P}</tex>
 
     '''return''' <tex>\mathtt{P}</tex>
  
Строка 121: Строка 121:
 
Понятно, что нам нет никакой необходимости просматривать все классы в разбиении. Вполне достаточно рассмотреть лишь те классы, из состояний которых есть хотя бы одно ребро в состояния сплиттера. Обозначим множество таких классов за <tex>T'</tex> (его нужно будет эффективно находить для каждой пары <tex>\langle C,\ a \rangle</tex>).
 
Понятно, что нам нет никакой необходимости просматривать все классы в разбиении. Вполне достаточно рассмотреть лишь те классы, из состояний которых есть хотя бы одно ребро в состояния сплиттера. Обозначим множество таких классов за <tex>T'</tex> (его нужно будет эффективно находить для каждой пары <tex>\langle C,\ a \rangle</tex>).
  
   '''function''' findEquivalenceClasses<tex>(Q,\ F,\ \delta)</tex>: '''vector'''
+
   <tex>\mathtt{findEquivalenceClasses}(Q,\ F,\ \delta)</tex>:
 
     <tex>\mathtt{P} \leftarrow \{ F, \ Q \setminus F \}</tex>
 
     <tex>\mathtt{P} \leftarrow \{ F, \ Q \setminus F \}</tex>
 
     <tex>\mathtt{S} \leftarrow \varnothing </tex>
 
     <tex>\mathtt{S} \leftarrow \varnothing </tex>
 
     '''for''' <tex>c \in \Sigma</tex>
 
     '''for''' <tex>c \in \Sigma</tex>
       push <tex>\langle F,\ c \rangle</tex>, <tex>\langle Q \setminus F,\ c \rangle</tex> '''into''' <tex> \mathtt{S}</tex>
+
       '''push''' <tex>\langle F,\ c \rangle</tex>, <tex>\langle Q \setminus F,\ c \rangle</tex> '''into''' <tex> \mathtt{S}</tex>
 
     '''while''' <tex>\mathtt{S} \ne \varnothing</tex>
 
     '''while''' <tex>\mathtt{S} \ne \varnothing</tex>
       <tex>\langle C,\ a \rangle</tex> <tex>\leftarrow</tex> pop '''from''' <tex>\mathtt{S}</tex>
+
       <tex>\langle C,\ a \rangle</tex> <tex>\leftarrow</tex> '''pop''' '''from''' <tex>\mathtt{S}</tex>
 
       <tex>\mathtt{Inverse} \leftarrow \{r \ | \ r \in Q, \ \delta(r, a) \in C\}</tex>
 
       <tex>\mathtt{Inverse} \leftarrow \{r \ | \ r \in Q, \ \delta(r, a) \in C\}</tex>
       <tex>T' \leftarrow \{R \ | \ R \in \mathtt{P}, \ R \cap \mathtt{Inverse} \neq \varnothing\}</tex> <font color=darkgreen>// находим классы, из состояний которых есть ребро в состояния сплиттера </font>
+
       <tex>T' \leftarrow \{R \ | \ R \in \mathtt{P}, \ R \cap \mathtt{Inverse} \neq \varnothing\}</tex>
       '''for''' <tex>R</tex> '''in''' <tex>T'</tex> <font color=darkgreen>// перебираем только классы входящие в <tex>T'</tex></font>
+
       '''for''' <tex>R</tex> '''in''' <tex>T'</tex>  
 
         <tex> R_1, R_2 \leftarrow </tex> <tex>\mathtt{split}(R,\ C,\ a)</tex>
 
         <tex> R_1, R_2 \leftarrow </tex> <tex>\mathtt{split}(R,\ C,\ a)</tex>
 
         '''if''' <tex> R_1 \ne \varnothing </tex> '''and''' <tex> R_2 \ne \varnothing </tex>
 
         '''if''' <tex> R_1 \ne \varnothing </tex> '''and''' <tex> R_2 \ne \varnothing </tex>
         replace <tex>R</tex> '''in''' <tex>\mathtt{P}</tex> with <tex>R_1</tex> '''and''' <tex>R_2</tex>
+
         '''replace''' <tex>R</tex> '''in''' <tex>\mathtt{P}</tex> '''with''' <tex>R_1</tex> '''and''' <tex>R_2</tex>
 
         '''if''' <tex>\langle R,\ c \rangle</tex> '''in''' <tex> \mathtt{S}</tex>
 
         '''if''' <tex>\langle R,\ c \rangle</tex> '''in''' <tex> \mathtt{S}</tex>
           remove <tex>\langle R, c \rangle</tex> '''from''' <tex>\mathtt{S}</tex>
+
           '''remove''' <tex>\langle R, c \rangle</tex> '''from''' <tex>\mathtt{S}</tex>
           push <tex>\langle R_1, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
+
           '''push''' <tex>\langle R_1, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
           push <tex>\langle R_2, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
+
           '''push''' <tex>\langle R_2, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
 
         '''else'''
 
         '''else'''
 
             '''if''' <tex> |\mathtt{P}[R_1]| \leqslant |\mathtt{P}[R_2]| </tex>
 
             '''if''' <tex> |\mathtt{P}[R_1]| \leqslant |\mathtt{P}[R_2]| </tex>
               push <tex>\langle R_1, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
+
               '''push''' <tex>\langle R_1, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
 
             '''else'''
 
             '''else'''
               push <tex>\langle R_2, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
+
               '''push''' <tex>\langle R_2, c \rangle</tex> '''into''' <tex>\mathtt{S}</tex>
 
     '''return''' <tex>\mathtt{P}</tex>
 
     '''return''' <tex>\mathtt{P}</tex>
  
  
Каждая итерация цикла <tex> \mathrm{while} </tex> может быть выполнена за <tex> O(|Q| + |\mathtt{Inverse}|)\,</tex> для текущей пары <tex>\langle C,\ a \rangle</tex>. Покажем, как можно достичь этой оценки.
+
Каждая итерация цикла <tex> \mathrm{while} </tex> может быть выполнена за <tex> O(|Q| + |\mathtt{Inverse}|) </tex> для текущей пары <tex>\langle C,\ a \rangle</tex>. Покажем, как можно достичь этой оценки.
  
 
Классы разбиения <tex>P</tex> будем поддерживать с помощью множеств на [[Хеш-таблица | хэш-таблицах]] (само же разбиение {{---}} обычный вектор, индекс {{---}} номер класса). Это позволит нам эффективно переносить состояния из одного класса в другой (за <tex>O(1)</tex>).
 
Классы разбиения <tex>P</tex> будем поддерживать с помощью множеств на [[Хеш-таблица | хэш-таблицах]] (само же разбиение {{---}} обычный вектор, индекс {{---}} номер класса). Это позволит нам эффективно переносить состояния из одного класса в другой (за <tex>O(1)</tex>).
Строка 152: Строка 152:
 
*<tex>\mathtt{Class}[r]</tex> {{---}} номер класса, которому принадлежит состояние <tex>r</tex>,
 
*<tex>\mathtt{Class}[r]</tex> {{---}} номер класса, которому принадлежит состояние <tex>r</tex>,
 
*<tex>\mathtt{Queue}</tex> {{---}} очередь пар <tex>\langle C,\ a \rangle</tex>, где <tex>C</tex> {{---}} номер класса (сплиттера),
 
*<tex>\mathtt{Queue}</tex> {{---}} очередь пар <tex>\langle C,\ a \rangle</tex>, где <tex>C</tex> {{---}} номер класса (сплиттера),
*<tex>\mathtt{Inv}[r][a]</tex> {{---}} массив состояний, из которых есть ребра по символу <tex>a</tex> в состояние <tex>r</tex> (мы не меняем исходный автомат, потому может быть построен раз перед началом работы алгоритма).
+
*<tex>\mathtt{Inv}[r][a]</tex> {{---}} массив состояний, из которых есть ребра по символу <tex>a</tex> в состояние <tex>r</tex> (мы не меняем исходный автомат, потому может быть построен раз перед началом работы алгоритма),
  
Для обработки <tex>T'</tex> за <tex>O(|Q| + |\mathtt{Inverse}|)\,</tex> нам понадобится следующая структура:
+
Для обработки <tex>T'</tex> за <tex>O(|Q| + |\mathtt{Inverse}|)</tex> нам понадобится следующая структура:
 
*<tex>\mathtt{Involved}</tex> {{---}} список из номеров классов, содержащихся во множестве <tex>T'</tex>,
 
*<tex>\mathtt{Involved}</tex> {{---}} список из номеров классов, содержащихся во множестве <tex>T'</tex>,
 
*<tex>\mathtt{Count}</tex> {{---}} целочисленный массив, где <tex>\mathtt{Count}[i]</tex> хранит количество состояний из класса <tex>i</tex>, которые содержатся в <tex>\mathtt{Inverse}</tex>,
 
*<tex>\mathtt{Count}</tex> {{---}} целочисленный массив, где <tex>\mathtt{Count}[i]</tex> хранит количество состояний из класса <tex>i</tex>, которые содержатся в <tex>\mathtt{Inverse}</tex>,
*<tex>\mathtt{Twin}</tex> {{---}} массив, хранящий в <tex>\mathtt{Twin}[i]</tex> номер нового класса, образовавшегося при разбиении класса <tex>i</tex>.
+
*<tex>\mathtt{Twin}</tex> {{---}} массив, хранящий в <tex>\mathtt{Twin}[i]</tex> номер нового класса, образовавшегося при разбиении класса <tex>i</tex>,
  
   '''function''' findEquivalenceClasses<tex>(Q,\ F,\ \delta)</tex>: '''vector'''
+
   <tex>\mathtt{findEquivalenceClasses}(Q,\ F,\ \delta)</tex>:
 
     <tex>\mathtt{P} \leftarrow \{ F, \ Q \setminus F \}</tex>
 
     <tex>\mathtt{P} \leftarrow \{ F, \ Q \setminus F \}</tex>
 
     '''for''' <tex>c \in \Sigma</tex>
 
     '''for''' <tex>c \in \Sigma</tex>
       push <tex>\langle F,\ c \rangle</tex>, <tex>\langle Q \setminus F,\ c \rangle</tex> '''into''' <tex> \mathtt{Queue}</tex>
+
       '''push''' <tex>\langle F,\ c \rangle</tex>, <tex>\langle Q \setminus F,\ c \rangle</tex> '''into''' <tex> \mathtt{Queue}</tex>
 
     '''while''' <tex>\mathtt{Queue} \ne \varnothing</tex>
 
     '''while''' <tex>\mathtt{Queue} \ne \varnothing</tex>
       <tex>\langle C,\ a \rangle</tex> <tex>\leftarrow</tex> pop '''from''' <tex>\mathtt{Queue}</tex>
+
       <tex>\langle C,\ a \rangle</tex> <tex>\leftarrow</tex> '''pop''' '''from''' <tex>\mathtt{Queue}</tex>
 
       <tex>\mathtt{Involved} \leftarrow \varnothing</tex>
 
       <tex>\mathtt{Involved} \leftarrow \varnothing</tex>
 
       '''for''' <tex>q \in C</tex> '''and''' <tex>r \in \mathtt{Inv}[q][a]</tex>
 
       '''for''' <tex>q \in C</tex> '''and''' <tex>r \in \mathtt{Inv}[q][a]</tex>
 
         <tex>i = \mathtt{Class}[r]</tex>
 
         <tex>i = \mathtt{Class}[r]</tex>
 
         '''if''' <tex>\mathtt{Count}[i] == 0</tex>
 
         '''if''' <tex>\mathtt{Count}[i] == 0</tex>
           insert <tex>i</tex> '''into''' <tex>\mathtt{Involved}</tex>
+
           '''insert''' <tex>i</tex> '''into''' <tex>\mathtt{Involved}</tex>
 
         <tex>\mathtt{Count}[i]++</tex>
 
         <tex>\mathtt{Count}[i]++</tex>
 
       '''for''' <tex> i \in \mathtt{Involved}</tex>
 
       '''for''' <tex> i \in \mathtt{Involved}</tex>
 
         '''if''' <tex>\mathtt{Count}[i] < |\mathtt{P}[i]|</tex>
 
         '''if''' <tex>\mathtt{Count}[i] < |\mathtt{P}[i]|</tex>
             insert <tex>\{\}</tex> '''into''' <tex>\mathtt{P}</tex> <font color=darkgreen>// создадим пустой класс в разбиении <tex>\mathtt{P}</tex></font>
+
             '''insert''' <tex>\{\}</tex> '''into''' <tex>\mathtt{P}</tex> <font color=darkgreen>//Создадим пустой класс в разбиении <tex>\mathtt{P}</tex></font>
             <tex>\mathtt{Twin}[i] = |\mathtt{P}|</tex> <font color=darkgreen> //запишем в <tex>\mathtt{Twin[i]}</tex> индекс нового класса</font>
+
             <tex>\mathtt{Twin}[i] = |\mathtt{P}|</tex> <font color=darkgreen>//Запишем в <tex>\mathtt{Twin[i]}</tex> индекс нового класса</font>
 
       '''for''' <tex>q \in C</tex> '''and''' <tex>r \in \mathtt{Inv}[q][a]</tex>
 
       '''for''' <tex>q \in C</tex> '''and''' <tex>r \in \mathtt{Inv}[q][a]</tex>
 
         <tex>i = \mathtt{Class}[r]</tex>
 
         <tex>i = \mathtt{Class}[r]</tex>
 
         <tex>j = \mathtt{Twin}[i]</tex>
 
         <tex>j = \mathtt{Twin}[i]</tex>
 
         '''if''' <tex>j \neq 0</tex>
 
         '''if''' <tex>j \neq 0</tex>
             remove <tex>r</tex> '''from''' <tex>\mathtt{P}[i]</tex>
+
             '''remove''' <tex>r</tex> '''from''' <tex>\mathtt{P}[i]</tex>
             add <tex>r</tex> '''to''' <tex>\mathtt{P}[j]</tex>
+
             '''add''' <tex>r</tex> '''to''' <tex>\mathtt{P}[j]</tex>
 
       '''for''' <tex> i \in \mathtt{Involved}</tex>
 
       '''for''' <tex> i \in \mathtt{Involved}</tex>
 
         <tex>j = \mathtt{Twin}[i]</tex>
 
         <tex>j = \mathtt{Twin}[i]</tex>
 
         '''if''' <tex> j \neq 0 </tex>
 
         '''if''' <tex> j \neq 0 </tex>
           '''if''' <tex>|\mathtt{P}[j]| > |\mathtt{P}[i]|</tex>  <font color=darkgreen>// парный класс должен быть меньшего размера</font>
+
           '''if''' <tex>|\mathtt{P}[j]| > |\mathtt{P}[i]|</tex>  <font color=darkgreen>//Парный класс должен быть меньшего размера</font>
             <tex>\mathtt{swap}(\mathtt{P}[i],\ \mathtt{P}[j])</tex> <font color=darkgreen>// swap за <tex>\mathtt{O(1)}</tex> {{---}} просто переставить указатели</font>
+
             <tex>\mathtt{swap}(\mathtt{P}[i],\ \mathtt{P}[j])</tex> <font color=darkgreen>//swap за O(1) - просто переставить указатели</font>
           '''for''' <tex>r \in \mathtt{P}[j]</tex> <font color=darkgreen> // обновляем номера классов для вершин, у которых они изменились</font>
+
           '''for''' <tex>r \in \mathtt{P}[j]</tex> <font color=darkgreen>//Обновляем номера классов для вершин, у которых они изменились</font>
 
             <tex>\mathtt{Class}[r] = j</tex>
 
             <tex>\mathtt{Class}[r] = j</tex>
 
           '''for''' <tex>c \in \Sigma</tex>
 
           '''for''' <tex>c \in \Sigma</tex>
             push <tex>\langle j, c \rangle</tex> '''to''' <tex>\mathtt{Queue}</tex>
+
             '''push''' <tex>\langle j, c \rangle</tex> '''to''' <tex>\mathtt{Queue}</tex>
 
         <tex>\mathtt{Count}[i] = 0</tex>
 
         <tex>\mathtt{Count}[i] = 0</tex>
 
         <tex>\mathtt{Twin}[i] = 0</tex>
 
         <tex>\mathtt{Twin}[i] = 0</tex>
Строка 195: Строка 195:
  
  
Стоит отметить, что массивы <tex>\mathtt{Count},\ \mathtt{Twin}\,</tex> аллоцируются ровно один раз при инициализации алгоритма.
+
Стоит отметить, что массивы <tex>\mathtt{Count},\ \mathtt{Twin}</tex> аллоцируются ровно один раз при инициализации алгоритма.
  
 
Также стоит отметить, что собственно наличие/отсутствие пары в очереди можно не проверять. Если для некоторого <tex>c</tex> пара <tex>\langle i, c \rangle</tex> уже была в очереди, то мы добавим её "вторую половинку" <tex>\langle \mathtt{Twin}[i], c \rangle</tex>. Если её в очереди не было, то мы вольны сами выбирать, какой подкласс добавлять в очередь, и таким образом добавляем опять же <tex>\langle \mathtt{Twin}[i], c \rangle</tex>.
 
Также стоит отметить, что собственно наличие/отсутствие пары в очереди можно не проверять. Если для некоторого <tex>c</tex> пара <tex>\langle i, c \rangle</tex> уже была в очереди, то мы добавим её "вторую половинку" <tex>\langle \mathtt{Twin}[i], c \rangle</tex>. Если её в очереди не было, то мы вольны сами выбирать, какой подкласс добавлять в очередь, и таким образом добавляем опять же <tex>\langle \mathtt{Twin}[i], c \rangle</tex>.
Кроме того, вместо очереди можно использовать вообще произвольную структуру, хранящую элементы, в том числе стэк, множество, так как порядок извлечения нам по сути не важен.
+
Кроме того, вместо очереди можно использовать вообще произвольную струтуру, хранящую элементы, в том числе стэк, множество, т.к. порядок извлечения нам по сути не важен.
  
 
===Время работы===
 
===Время работы===
Строка 237: Строка 237:
 
<tex>\sum |\mathtt{Inverse}|</tex> по всем итерациям цикла <tex>\mathrm{while}</tex> не превосходит <tex>|\Sigma| |Q| \log_2(|Q|)</tex>.
 
<tex>\sum |\mathtt{Inverse}|</tex> по всем итерациям цикла <tex>\mathrm{while}</tex> не превосходит <tex>|\Sigma| |Q| \log_2(|Q|)</tex>.
 
|proof =
 
|proof =
Пусть <tex>x, y \in Q</tex>, <tex>a \in \Sigma</tex> и <tex> \delta(x, a) = y</tex>. Зафиксируем эту тройку. Заметим, что количество раз, которое <tex>x</tex> встречается в <tex>\mathtt{Inverse}\,</tex> при условии, что <tex> \delta(x, a) = y</tex>, совпадает с числом удаленных из очереди пар <tex>\langle C,\ a \rangle</tex>, где <tex>y \in C</tex>. Но по [[#Лемма3 | лемме(3)]] эта величина не превосходит <tex>\log_2(|Q|)</tex>. Просуммировав по всем <tex> x \in Q </tex> и по всем <tex> a \in \Sigma</tex> мы получим утверждение леммы.
+
Пусть <tex>x, y \in Q</tex>, <tex>a \in \Sigma</tex> и <tex> \delta(x, a) = y</tex>. Зафиксируем эту тройку. Заметим, что количество раз, которое <tex>x</tex> встречается в <tex>\mathtt{Inverse}</tex> при условии, что <tex> \delta(x, a) = y</tex>, совпадает с числом удаленных из очереди пар <tex>\langle C,\ a \rangle</tex>, где <tex>y \in C</tex>. Но по [[#Лемма3 | лемме(3)]] эта величина не превосходит <tex>\log_2(|Q|)</tex>. Просуммировав по всем <tex> x \in Q </tex> и по всем <tex> a \in \Sigma</tex> мы получим утверждение леммы.
 
}}
 
}}
  
Строка 250: Строка 250:
 
*По [[#Лемма2 | второй лемме]] количество итераций цикла <tex>\mathrm{while}</tex> не превосходит <tex>O(|\Sigma| |Q|)</tex>.
 
*По [[#Лемма2 | второй лемме]] количество итераций цикла <tex>\mathrm{while}</tex> не превосходит <tex>O(|\Sigma| |Q|)</tex>.
  
*Операции с множеством <tex>T'</tex> и разбиение классов на подклассы требуют <tex>O(\sum(|\mathtt{Inverse}|))\,</tex> времени. Но по [[#Лемма4 | лемме(4)]] <tex>\sum(|\mathtt{Inverse}|)\,</tex> не превосходит <tex>|\Sigma| |Q| \log_2(|Q|)</tex>, то есть данная часть алгоритма выполняется за <tex>O(|\Sigma| |Q| \log_2(|Q|))</tex>.
+
*Операции с множеством <tex>T'</tex> и разбиение классов на подклассы требуют <tex>O(\sum(|\mathtt{Inverse}|))</tex> времени. Но по [[#Лемма4 | лемме(4)]] <tex>\sum(|\mathtt{Inverse}|)</tex> не превосходит <tex>|\Sigma| |Q| \log_2(|Q|)</tex>, то есть данная часть алгоритма выполняется за <tex>O(|\Sigma| |Q| \log_2(|Q|))</tex>.
  
 
*В [[#Лемма1 | лемме(1)]] мы показали, что в процессе работы алгоритма не может появится больше, чем <tex>2 |Q| - 1</tex> классов, из чего следует, что количество операций <tex>\mathtt{replace}</tex> равно <tex>O(|\Sigma| |Q|)</tex>.
 
*В [[#Лемма1 | лемме(1)]] мы показали, что в процессе работы алгоритма не может появится больше, чем <tex>2 |Q| - 1</tex> классов, из чего следует, что количество операций <tex>\mathtt{replace}</tex> равно <tex>O(|\Sigma| |Q|)</tex>.
Строка 270: Строка 270:
 
     <tex>\mathtt{P} \leftarrow \{ F, \ Q \setminus F \}</tex>
 
     <tex>\mathtt{P} \leftarrow \{ F, \ Q \setminus F \}</tex>
 
     '''for''' <tex>c \in \Sigma</tex>
 
     '''for''' <tex>c \in \Sigma</tex>
       insert <tex>\langle F,\ c \rangle</tex>, <tex>\langle Q \setminus F,\ c \rangle</tex> '''into''' <tex> \mathtt{Queue}</tex>
+
       '''insert''' <tex>\langle F,\ c \rangle</tex>, <tex>\langle Q \setminus F,\ c \rangle</tex> '''into''' <tex> \mathtt{Queue}</tex>
 
     '''while''' <tex>\mathtt{Queue} \ne \varnothing</tex>
 
     '''while''' <tex>\mathtt{Queue} \ne \varnothing</tex>
       <tex>\langle C,\ a \rangle</tex> <tex>\leftarrow</tex> pop '''from''' <tex>\mathtt{Queue}</tex>
+
       <tex>\langle C,\ a \rangle</tex> <tex>\leftarrow</tex> '''pop from''' <tex>\mathtt{Queue}</tex>
 
       <tex>\mathtt{Involved} = \{\}</tex>
 
       <tex>\mathtt{Involved} = \{\}</tex>
 
       '''for''' <tex>q \in C</tex> '''and''' <tex>r \in \mathtt{Inv}[q][a]</tex>
 
       '''for''' <tex>q \in C</tex> '''and''' <tex>r \in \mathtt{Inv}[q][a]</tex>
Строка 278: Строка 278:
 
         '''if''' <tex>\mathtt{Involved}[i] == \varnothing</tex>
 
         '''if''' <tex>\mathtt{Involved}[i] == \varnothing</tex>
 
             <tex>\mathtt{Involved}[i] = \{\}</tex>
 
             <tex>\mathtt{Involved}[i] = \{\}</tex>
         add <tex>r</tex> '''to''' <tex>\mathtt{Involved}[i]</tex>
+
         '''add''' <tex>r</tex> '''to''' <tex>\mathtt{Involved}[i]</tex>
 
       '''for''' <tex> i \in \mathtt{Involved}</tex> <font color=darkgreen>//Перебираем ключи <tex>\mathtt{Involved}</tex></font>
 
       '''for''' <tex> i \in \mathtt{Involved}</tex> <font color=darkgreen>//Перебираем ключи <tex>\mathtt{Involved}</tex></font>
 
         '''if''' <tex>|\mathtt{Involved}[i]| < |\mathtt{P}[i]|</tex>
 
         '''if''' <tex>|\mathtt{Involved}[i]| < |\mathtt{P}[i]|</tex>
Строка 284: Строка 284:
 
             <tex>j = |\mathtt{P}|</tex> <font color=darkgreen>//Запишем в <tex>j</tex> индекс нового класса</font>
 
             <tex>j = |\mathtt{P}|</tex> <font color=darkgreen>//Запишем в <tex>j</tex> индекс нового класса</font>
 
             '''for''' <tex>r</tex> '''in''' <tex>\mathtt{Involved}[i]</tex>
 
             '''for''' <tex>r</tex> '''in''' <tex>\mathtt{Involved}[i]</tex>
               remove <tex>r</tex> '''from''' <tex>\mathtt{P}[i]</tex>
+
               '''remove''' <tex>r</tex> '''from''' <tex>\mathtt{P}[i]</tex>
               add <tex>r</tex> '''to''' <tex>\mathtt{P}[j]</tex>
+
               '''add''' <tex>r</tex> '''to''' <tex>\mathtt{P}[j]</tex>
 
             '''if''' <tex>|\mathtt{P}[j]| > |\mathtt{P}[i]|</tex>  <font color=darkgreen>//Парный класс должен быть меньшего размера</font>
 
             '''if''' <tex>|\mathtt{P}[j]| > |\mathtt{P}[i]|</tex>  <font color=darkgreen>//Парный класс должен быть меньшего размера</font>
                 <tex>\mathtt{swap}(\mathtt{P}[i],\ \mathtt{P}[j])</tex> <font color=darkgreen>//swap за <tex>\mathtt{O(1)}</tex> {{---}} просто переставить указатели</font>
+
                 <tex>\mathtt{swap}(\mathtt{P}[i],\ \mathtt{P}[j])</tex> <font color=darkgreen>//swap за O(1) - просто переставить указатели</font>
 
             '''for''' <tex>r \in \mathtt{P}[j]</tex> <font color=darkgreen>//Обновляем номера классов для вершин, у которых они изменились</font>
 
             '''for''' <tex>r \in \mathtt{P}[j]</tex> <font color=darkgreen>//Обновляем номера классов для вершин, у которых они изменились</font>
 
               <tex>\mathtt{Class}[r] = j</tex>
 
               <tex>\mathtt{Class}[r] = j</tex>
 
             '''for''' <tex>c \in \Sigma</tex>
 
             '''for''' <tex>c \in \Sigma</tex>
               push <tex>\langle j, c \rangle</tex> '''into''' <tex>\mathtt{Queue}</tex>
+
               '''push''' <tex>\langle j, c \rangle</tex> '''into''' <tex>\mathtt{Queue}</tex>
 
     '''return''' <tex>\mathtt{P}</tex>
 
     '''return''' <tex>\mathtt{P}</tex>
  

Пожалуйста, учтите, что любой ваш вклад в проект «Викиконспекты» может быть отредактирован или удалён другими участниками. Если вы не хотите, чтобы кто-либо изменял ваши тексты, не помещайте их сюда.
Вы также подтверждаете, что являетесь автором вносимых дополнений, или скопировали их из источника, допускающего свободное распространение и изменение своего содержимого (см. Викиконспекты:Авторские права). НЕ РАЗМЕЩАЙТЕ БЕЗ РАЗРЕШЕНИЯ ОХРАНЯЕМЫЕ АВТОРСКИМ ПРАВОМ МАТЕРИАЛЫ!

Чтобы изменить эту страницу, пожалуйста, ответьте на приведённый ниже вопрос (подробнее):

Отменить | Справка по редактированию (в новом окне)