Duży nawias to wszystkie możliwe przejścia ze stanu początkowego ponownie na stan początkowy. Następnie są wszystkie możliwe zakończenia na stanach akceptujących.
\Sigma = \{a_0, a_1, \ldots, a_n\}. Niech S_k - zbiór wszystkich słów nad alfabetem nie zawierającym słowa a_k:
S_k = \left( \sum_{i \neq k} a_i \right)^*
w_0 = a_0
w_k = (w_{k-1}a_k)^* \cap S_ka_kS_ka_k
Nasze słowo to w_n.
Automat:
Automat można „zwinąć” w ten sposób:
Na pierwszym grafie usunąłem ścieżki prowadzące do „martwych” stanów. Na drugim „zwinąłem” rogi – usunąłem je (te wierzchołki) jednocześnie wstawiając do grafu krawędzie, które zachowują równoważność z grafem z rogami. Na trzecim grafie zrobiłem osobny stan początkowy i osobny stan końcowy. Na czwartym usunąłem kolejny wierzchołek. Po czym stwierdziłem, że tyle kroków wystarczy by zobaczyć sens algorytmu, a efekt końcowy będzie równie nieczytelny co skrypt perlowy dude'a, więc pozostawię usunięcie reszty zbędnych wierzchołków czytelnikowi (ew. komuś przy tablicy).
\left(a(dc+cd)^*b+b(dc+cd)^*a+c(ab+ba)^*d+d(ab+ba)^*c + adbc + bdac + acbd + bcad + cadb + dacb + cbda + dbca\right)^*
Nie przechodzi adbacb
Wygenerowane programem w perlu, lol http://dude.bshellz.net/zad13.pl
#!/usr/bin/perl -w use strict; my $start; my $finish; my %trans; while (<>) { chomp; next if !$_; if (/^S:\s*(\(.*\))/) { $start = $1; } elsif (/^F:\s*(\(.*\))/) { $finish = $1; } elsif (/(\(.*?\))\s*-(.*?)>\s*(\(.*?\))/) { if ($trans{$1}->{$3}) { $trans{$1}->{$3} .= " + $2"; } else { $trans{$1}->{$3} = "$2"; } } } while (keys %trans > 1) { for my $toremove (keys %{$trans{$start}}) { next if $toremove eq $start; my $loop = ""; if ($trans{$toremove}->{$toremove}) { $loop = "($trans{$toremove}->{$toremove})*"; } for my $neigh (keys %trans) { next if $neigh eq $toremove; next unless ($trans{$neigh}->{$toremove}); my $via = $trans{$neigh}->{$toremove}; $via = "($via)" if ($via =~ /(\+\s)/); for my $dist (keys %{$trans{$toremove}}) { next if $dist eq $toremove; my $via2 = $trans{$toremove}->{$dist}; $via2 = "($via2)" if ($via2 =~ /(\+\s)/); my $path = "$via$loop$via2"; if ($trans{$neigh}->{$dist}) { $trans{$neigh}->{$dist} .= " + $path"; } else { $trans{$neigh}->{$dist} = "$path"; } } delete $trans{$neigh}->{$toremove}; } delete $trans{$toremove}; } } my $loop = ""; if ($trans{$start}->{$start}) { $loop = "($trans{$start}->{$start})*"; } my $path = ""; if ($trans{$start}->{$finish}) { $path = $trans{$start}->{$finish}; } $path = "$loop$path"; $path =~ s/e([a-df-z]+)/$1/g; $path =~ s/([a-df-z]+)e/$1/g; print "$path\n";
S: ( 0, 0) F: (A) ( 0, 0) -a> ( 1, 0) ( 0, 0) -b> (-1, 0) ( 0, 0) -c> ( 0, 1) ( 0, 0) -d> ( 0,-1) ( 0, 0) -e> (A) ( 1, 0) -a> (F) ( 1, 0) -b> ( 0, 0) ( 1, 0) -c> ( 1, 1) ( 1, 0) -d> ( 1,-1) (-1, 0) -a> ( 0, 0) (-1, 0) -b> (F) (-1, 0) -c> (-1, 1) (-1, 0) -d> (-1,-1) ( 0, 1) -a> ( 1, 1) ( 0, 1) -b> (-1, 1) ( 0, 1) -c> (F) ( 0, 1) -d> ( 0, 0) ( 0,-1) -a> ( 1,-1) ( 0,-1) -b> (-1,-1) ( 0,-1) -c> ( 0, 0) ( 0,-1) -d> (F) ( 1, 1) -a> (F) ( 1, 1) -b> ( 0, 1) ( 1, 1) -c> (F) ( 1, 1) -d> ( 1, 0) ( 1,-1) -a> (F) ( 1,-1) -b> ( 0,-1) ( 1,-1) -c> ( 1, 0) ( 1,-1) -d> (F) (-1, 1) -a> ( 0, 1) (-1, 1) -b> (F) (-1, 1) -c> (F) (-1, 1) -d> (-1, 0) (-1,-1) -a> ( 0,-1) (-1,-1) -b> (F) (-1,-1) -c> (-1, 0) (-1,-1) -d> (F)
Łatwo zauważyć, że możemy bez problemu rozpoznać język L_p', który działa dokładnie tak samo, z taką różnicą, że wczytujemy słowa od lewej (od najbardziej znaczącego bitu).
Widzimy, że L_p' = \{ w^R | w \in L_p \} i skorzystamy z tego, że NDFA się łatwo odwracają. Pokażę teraz krótko konstrukcję automatu rozpoznającego odwrócony język.
Niech A = \langle \Sigma, Q, q_0, F, \delta \rangle, to A^R = \langle \Sigma, Q \cup \{q_z\}, q_z, \{q_0\}, \delta' \}. Idea konstrukcji jest taka:
Formalniej,
Innymi słowy, jesteśmy w stanie rozpoznać odwrócony język używając jednego stanu więcej.
No dobrze, uzbrojeni w tą wiedzę, możemy krótko opisać automat z zadania. Nietrudno skonstruować NDFA (a nawet DFA) rozpoznający L_p'. Niech Q = \{q_0\} \cup \{0, \ldots, p-1\}, krawędzie zaś są postaci: \delta(q_0, a) = a \wedge \delta(r, a) = (2r + a) mod\ p, gdzie oczywiście a \in \{0,1\}. Automat na p+1 stanów, także ten rozpoznający odwrócony - p+2. Koniec.