169
|
1 using Implab;
|
|
2 using System;
|
|
3 using System.Collections.Generic;
|
|
4 using System.Linq;
|
|
5
|
|
6 namespace Implab.Automaton {
|
|
7 public class DFATable : IDFATableBuilder {
|
|
8 int m_stateCount;
|
|
9 int m_symbolCount;
|
|
10 int m_initialState;
|
|
11
|
|
12 readonly HashSet<int> m_finalStates = new HashSet<int>();
|
|
13 readonly HashSet<AutomatonTransition> m_transitions = new HashSet<AutomatonTransition>();
|
|
14
|
|
15
|
|
16 #region IDFADefinition implementation
|
|
17
|
|
18 public bool IsFinalState(int s) {
|
|
19 Safe.ArgumentInRange(s, 0, m_stateCount, "s");
|
|
20
|
171
|
21 return m_finalStates.Contains(s);
|
169
|
22 }
|
|
23
|
|
24 public IEnumerable<int> FinalStates {
|
|
25 get {
|
|
26 return m_finalStates;
|
|
27 }
|
|
28 }
|
|
29
|
|
30 public int StateCount {
|
|
31 get { return m_stateCount; }
|
|
32 }
|
|
33
|
|
34 public int AlphabetSize {
|
|
35 get { return m_symbolCount; }
|
|
36 }
|
|
37
|
|
38 public int InitialState {
|
|
39 get { return m_initialState; }
|
|
40 }
|
|
41
|
|
42 #endregion
|
|
43
|
|
44 public void SetInitialState(int s) {
|
|
45 Safe.ArgumentAssert(s >= 0, "s");
|
|
46 m_initialState = s;
|
|
47 }
|
|
48
|
|
49 public void MarkFinalState(int state) {
|
|
50 m_finalStates.Add(state);
|
|
51 }
|
|
52
|
|
53 public void Add(AutomatonTransition item) {
|
|
54 Safe.ArgumentAssert(item.s1 >= 0, "item");
|
|
55 Safe.ArgumentAssert(item.s2 >= 0, "item");
|
|
56 Safe.ArgumentAssert(item.edge >= 0, "item");
|
|
57
|
|
58 m_stateCount = Math.Max(m_stateCount, Math.Max(item.s1, item.s2) + 1);
|
|
59 m_symbolCount = Math.Max(m_symbolCount, item.edge);
|
|
60
|
|
61 m_transitions.Add(item);
|
|
62 }
|
|
63
|
|
64 public void Clear() {
|
|
65 m_stateCount = 0;
|
|
66 m_symbolCount = 0;
|
|
67 m_finalStates.Clear();
|
|
68 m_transitions.Clear();
|
|
69 }
|
|
70
|
|
71 public bool Contains(AutomatonTransition item) {
|
|
72 return m_transitions.Contains(item);
|
|
73 }
|
|
74
|
|
75 public void CopyTo(AutomatonTransition[] array, int arrayIndex) {
|
|
76 m_transitions.CopyTo(array, arrayIndex);
|
|
77 }
|
|
78
|
|
79 public bool Remove(AutomatonTransition item) {
|
|
80 m_transitions.Remove(item);
|
|
81 }
|
|
82
|
|
83 public int Count {
|
|
84 get {
|
|
85 return m_transitions.Count;
|
|
86 }
|
|
87 }
|
|
88
|
|
89 public bool IsReadOnly {
|
|
90 get {
|
171
|
91 return false;
|
169
|
92 }
|
|
93 }
|
|
94
|
|
95 public IEnumerator<AutomatonTransition> GetEnumerator() {
|
|
96 return m_transitions.GetEnumerator();
|
|
97 }
|
|
98
|
|
99 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
|
|
100 return GetEnumerator();
|
|
101 }
|
|
102
|
172
|
103 public DFAStateDescriptor[] CreateTransitionTable() {
|
|
104 var table = new DFAStateDescriptor[StateCount];
|
|
105
|
|
106 foreach (var t in this) {
|
|
107 if (table[t.s1].transitions == null)
|
|
108 table[t.s1] = new DFAStateDescriptor(AlphabetSize, IsFinalState(t.s1));
|
|
109 if (table[t.s2].transitions == null)
|
|
110 table[t.s2] = new DFAStateDescriptor(AlphabetSize, IsFinalState(t.s2));
|
|
111 table[t.s1].transitions[t.edge] = t.s2;
|
|
112 }
|
|
113
|
|
114 return table;
|
|
115 }
|
|
116
|
169
|
117 /// <summary>Формирует множества конечных состояний перед началом работы алгоритма минимизации.</summary>
|
|
118 /// <remarks>
|
|
119 /// В процессе построения минимального автомата требуется разделить множество состояний,
|
|
120 /// на два подмножества - конечные состояния и все остальные, после чего эти подмножества
|
|
121 /// будут резделены на более мелкие. Иногда требуется гарантировать различия конечных сосотяний,
|
|
122 /// для этого необходимо переопределить даннцю фукнцию, для получения множеств конечных состояний.
|
|
123 /// </remarks>
|
|
124 /// <returns>The final states.</returns>
|
|
125 protected virtual IEnumerable<HashSet<int>> GroupFinalStates() {
|
|
126 return new HashSet<int>[] { m_finalStates };
|
|
127 }
|
|
128
|
171
|
129 protected void Optimize(
|
169
|
130 IDFATableBuilder optimalDFA,
|
171
|
131 IDictionary<int,int> alphabetMap,
|
|
132 IDictionary<int,int> stateMap
|
169
|
133 ) {
|
|
134 Safe.ArgumentNotNull(optimalDFA, "dfa");
|
171
|
135 Safe.ArgumentNotNull(alphabetMap, "alphabetMap");
|
|
136 Safe.ArgumentNotNull(stateMap, "stateMap");
|
169
|
137
|
|
138
|
|
139 var setComparer = new CustomEqualityComparer<HashSet<int>>(
|
|
140 (x, y) => x.SetEquals(y),
|
|
141 s => s.Sum(x => x.GetHashCode())
|
|
142 );
|
|
143
|
|
144 var optimalStates = new HashSet<HashSet<int>>(setComparer);
|
|
145 var queue = new HashSet<HashSet<int>>(setComparer);
|
|
146
|
|
147 // получаем конечные состояния, сгруппированные по маркерам
|
|
148 optimalStates.UnionWith(
|
|
149 GroupFinalStates()
|
|
150 );
|
|
151
|
|
152 var state = new HashSet<int>(
|
|
153 Enumerable
|
|
154 .Range(0, m_stateCount - 1)
|
|
155 .Where(i => !m_finalStates.Contains(i))
|
|
156 );
|
|
157
|
|
158 optimalStates.Add(state);
|
|
159 queue.Add(state);
|
|
160
|
|
161 var rmap = m_transitions
|
|
162 .GroupBy(t => t.s2)
|
|
163 .ToLookup(
|
|
164 g => g.Key, // s2
|
|
165 g => g.ToLookup(t => t.edge, t => t.s1)
|
|
166 );
|
|
167
|
|
168 while (queue.Count > 0) {
|
|
169 var stateA = queue.First();
|
|
170 queue.Remove(stateA);
|
|
171
|
|
172 for (int c = 0; c < m_symbolCount; c++) {
|
|
173 var stateX = new HashSet<int>();
|
|
174 foreach(var a in stateA)
|
|
175 stateX.UnionWith(rmap[a][c]); // all states from wich 'c' leads to 'a'
|
|
176
|
|
177 foreach (var stateY in optimalStates.ToArray()) {
|
|
178 if (stateX.Overlaps(stateY) && !stateY.IsSubsetOf(stateX)) {
|
|
179 var stateR1 = new HashSet<int>(stateY);
|
|
180 var stateR2 = new HashSet<int>(stateY);
|
|
181
|
|
182 stateR1.IntersectWith(stateX);
|
|
183 stateR2.ExceptWith(stateX);
|
|
184
|
|
185 optimalStates.Remove(stateY);
|
|
186 optimalStates.Add(stateR1);
|
|
187 optimalStates.Add(stateR2);
|
|
188
|
|
189 if (queue.Contains(stateY)) {
|
|
190 queue.Remove(stateY);
|
|
191 queue.Add(stateR1);
|
|
192 queue.Add(stateR2);
|
|
193 } else {
|
|
194 queue.Add(stateR1.Count <= stateR2.Count ? stateR1 : stateR2);
|
|
195 }
|
|
196 }
|
|
197 }
|
|
198 }
|
|
199 }
|
|
200
|
|
201 // карта получения оптимального состояния по соотвествующему ему простому состоянию
|
171
|
202 var nextState = 0;
|
|
203 foreach (var item in optimalStates) {
|
|
204 var id = nextState++;
|
|
205 foreach (var s in item)
|
|
206 stateMap[s] = id;
|
|
207 }
|
169
|
208
|
|
209 // получаем минимальный алфавит
|
171
|
210 // входные символы не различимы, если Move(s,a1) == Move(s,a2), для любого s
|
|
211 // для этого используем алгоритм кластеризации, сначала
|
|
212 // считаем, что все символы не различимы
|
|
213
|
|
214 var minClasses = new HashSet<HashSet<int>>(setComparer);
|
|
215 var alphaQueue = new Queue<HashSet<int>>();
|
|
216 alphaQueue.Enqueue(new HashSet<int>(Enumerable.Range(0,AlphabetSize)));
|
|
217
|
|
218 // для всех состояний, будем проверять каждый класс на различимость,
|
|
219 // т.е. символы различимы, если они приводят к разным состояниям
|
|
220 for (int s = 0 ; s < optimalStates.Count; s++) {
|
|
221 var newQueue = new Queue<HashSet<int>>();
|
|
222
|
|
223 foreach (var A in alphaQueue) {
|
|
224 // классы из одного символа делить бесполезно, переводим их сразу в
|
|
225 // результирующий алфавит
|
|
226 if (A.Count == 1) {
|
|
227 minClasses.Add(A);
|
|
228 continue;
|
|
229 }
|
|
230
|
|
231 // различаем классы символов, которые переводят в различные оптимальные состояния
|
|
232 // optimalState -> alphaClass
|
|
233 var classes = new Dictionary<int, HashSet<int>>();
|
|
234
|
|
235 foreach (var term in A) {
|
|
236 // ищем все переходы класса по символу term
|
|
237 var res = m_transitions.Where(t => stateMap[t.s1] == s && t.edge == term).Select(t => stateMap[t.s2]).ToArray();
|
169
|
238
|
171
|
239 var s2 = res.Length > 0 ? res[0] : -1;
|
|
240
|
|
241 HashSet<int> a2;
|
|
242 if (!classes.TryGetValue(s2, out a2)) {
|
|
243 a2 = new HashSet<int>();
|
|
244 newQueue.Enqueue(a2);
|
|
245 classes[s2] = a2;
|
|
246 }
|
|
247 a2.Add(term);
|
|
248 }
|
|
249 }
|
|
250
|
|
251 if (newQueue.Count == 0)
|
|
252 break;
|
|
253 alphaQueue = newQueue;
|
|
254 }
|
|
255
|
|
256 // после окончания работы алгоритма в очереди останутся минимальные различимые классы
|
|
257 // входных символов
|
|
258 foreach (var A in alphaQueue)
|
|
259 minClasses.Add(A);
|
|
260
|
|
261 // построение отображения алфавитов входных символов.
|
|
262 // поскольку символ DFAConst.UNCLASSIFIED_INPUT может иметь
|
|
263 // специальное значение, тогда сохраним минимальный класс,
|
|
264 // содержащий этот символ на томже месте.
|
|
265
|
|
266 var nextCls = 0;
|
|
267 foreach (var item in minClasses) {
|
|
268 if (nextCls == DFAConst.UNCLASSIFIED_INPUT)
|
|
269 nextCls++;
|
|
270
|
|
271 // сохраняем DFAConst.UNCLASSIFIED_INPUT
|
|
272 var cls = item.Contains(DFAConst.UNCLASSIFIED_INPUT) ? DFAConst.UNCLASSIFIED_INPUT : nextCls;
|
|
273
|
|
274 foreach (var a in item)
|
|
275 alphabetMap[a] = cls;
|
|
276
|
|
277 nextCls++;
|
|
278 }
|
169
|
279
|
|
280 // построение автомата
|
171
|
281 optimalDFA.SetInitialState(stateMap[m_initialState]);
|
169
|
282
|
171
|
283 foreach (var sf in m_finalStates.Select(s => stateMap[s]).Distinct())
|
|
284 optimalDFA.MarkFinalState(sf);
|
169
|
285
|
171
|
286 foreach (var t in m_transitions.Select(t => new AutomatonTransition(stateMap[t.s1],stateMap[t.s2],alphabetMap[t.edge])).Distinct())
|
169
|
287 optimalDFA.Add(t);
|
|
288 }
|
|
289
|
|
290 protected void PrintDFA<TInput, TState>(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) {
|
|
291 Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet");
|
|
292 Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet");
|
|
293
|
|
294 foreach(var t in m_transitions)
|
|
295 Console.WriteLine(
|
|
296 "[{0}] -{{{1}}}-> [{2}]{3}",
|
171
|
297 String.Join(",", stateAlphabet.GetSymbols(t.s1)),
|
|
298 String.Join("", inputAlphabet.GetSymbols(t.edge)),
|
|
299 String.Join(",", stateAlphabet.GetSymbols(t.s2)),
|
169
|
300 m_finalStates.Contains(t.s2) ? "$" : ""
|
|
301 );
|
|
302 }
|
|
303
|
|
304 }
|
|
305 }
|