/** * Unordered pair of states. Used in minimization operation. */ static class UnorderedStatePair { State s1, s2; /** * Constructs a new unordered pair. */ UnorderedStatePair(State s1, State s2) { this.s1 = s1; this.s2 = s2; } /** * Checks whether two unordered pairs are equal. */ @Override public boolean equals(Object obj) { if (!(obj instanceof UnorderedStatePair)) return false; UnorderedStatePair ss = (UnorderedStatePair) obj; return (s1==ss.s1 && s2==ss.s2) || (s1==ss.s2 && s2==ss.s1); } /** * Computes hash code for this object. */ @Override public int hashCode() { return s1.hashCode() + s2.hashCode(); } } /** * Constructs a new minimal automaton with the same language as this automaton. [Martin, Sec. 2.6] * The input automaton is unmodified. * Note: this textbook algorithm is simple to understand but not very efficient * compared to other existing algorithms. */ public FA minimize() { FA f = removeUnreachableStates(); Set marks = new HashSet(); List statelist = new ArrayList(f.states); // ensure consistent state order // first pass, divide into accept/reject states for (State p : statelist) for (State q : statelist) { if (p == q) break; // traversing all *unordered* pairs of states if (f.accept.contains(p)!=f.accept.contains(q)) marks.add(new UnorderedStatePair(p, q)); } // iteratively perform marking passes until nothing more happens boolean done = false; while (!done) { // (this could be made more efficient with a worklist) done = true; for (State p : statelist) for (State q : statelist) { if (p == q) break; if (!marks.contains(new UnorderedStatePair(p, q))) for (char a : f.alphabet.symbols) { State r = f.delta(p, a); State s = f.delta(q, a); if (marks.contains(new UnorderedStatePair(r, s))) { marks.add(new UnorderedStatePair(p, q)); done = false; break; } } } } // start building the new automaton FA n = new FA(); n.alphabet = alphabet; Map old2new = new HashMap(); // map from old states to new states Map new2old = new HashMap(); // map from new states to representatives in old states // make new states for (State r : statelist) { boolean repr = true; // true if r is representative for its equivalence class for (State s : statelist) { if (r == s) break; if (!marks.contains(new UnorderedStatePair(r, s))) { old2new.put(r, old2new.get(s)); repr = false; break; } } if (repr) { State p = new State(); n.states.add(p); if (f.accept.contains(r)) n.accept.add(p); old2new.put(r, p); new2old.put(p, r); } if (r==f.initial) n.initial = old2new.get(r); } // make new transitions for (State p : n.states) { State r = new2old.get(p); for (char a : n.alphabet.symbols) n.transitions.put(new StateSymbolPair(p, a), old2new.get(f.delta(r, a))); } return n; }