Лаб. 2 ТАЯК
.docxЛабораторная работа №2
Конечные детерминированные автоматы. Преобразование недетерминированного конечного автомата к детерминированному
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
class Program
{
class Nfa
{
// Правила перехода (qN: (C: List<<q|f>M>))
public Dictionary<string, Dictionary<char, List<string>>> transitions = new();
// Множество финальных состояний
public HashSet<string> finals = new();
// Множество всех встреченных состояний
public HashSet<string> states = new();
// Множество символов, используемых в переходах
public HashSet<char> alphabet = new();
// Начальное состояние
public string start = "q0";
// Занесение правил перехода, финальных и всех встреченных состояний, а так же символов
public void AddTransition(string src, char sym, string dest)
{
if (!transitions.ContainsKey(src)) transitions[src] = new();
if (!transitions[src].ContainsKey(sym)) transitions[src][sym] = new();
transitions[src][sym].Add(dest);
states.Add(src);
states.Add(dest);
if (dest.StartsWith("f")) finals.Add(dest);
alphabet.Add(sym);
}
public bool IsDeterministic(out List<(string src, char sym, List<string> dests)> nondetRules)
{
nondetRules = new();
foreach (var kv in transitions)
{
string s = kv.Key;
foreach (var symK in kv.Value)
{
var dests = symK.Value;
if (dests.Count > 1)
nondetRules.Add((s, symK.Key, new List<string>(dests)));
}
}
return nondetRules.Count == 0;
}
public bool AcceptsDeterministic(string input)
{
string cur = start;
foreach (char c in input)
{
if (!transitions.TryGetValue(cur, out var map) || !map.TryGetValue(c, out var dests) || dests.Count == 0)
return false;
cur = dests[0];
}
return cur.StartsWith("f");
}
}
class Dfa
{
public Dictionary<int, Dictionary<char, int>> transitions = new();
public HashSet<int> finals = new();
public HashSet<int> states = new();
public HashSet<char> alphabet = new();
public int start = 0;
public bool Accepts(string input)
{
int cur = start;
foreach (char c in input)
{
if (!transitions.TryGetValue(cur, out var map) || !map.TryGetValue(c, out var dest))
return false;
cur = dest;
}
return finals.Contains(cur);
}
}
static void Main()
{
Console.WriteLine("=== НКА/ДКА анализатор ===");
Console.Write("Путь к файлу с переходами (Enter — пример): ");
string path = Console.ReadLine();
List<string> lines;
if (string.IsNullOrWhiteSpace(path))
{
lines = new()
{
"q0,a=f0",
"q0,a=q1",
"q0,b=q1",
"q1,0=f0",
"q1,0=q1"
};
Console.WriteLine("Используется встроенный пример:");
foreach (var l in lines) Console.WriteLine(l);
}
else
{
if (!File.Exists(path)) { Console.WriteLine("Файл не найден."); return; }
lines = File.ReadAllLines(path).Select(s => s.Trim()).Where(s => s.Length > 0).ToList();
}
var nfa = new Nfa();
// Некорректные строки
var bad = new List<(int, string, string)>();
var pat = new Regex(@"^q(\d+),(.{1})=(f|q)(\d+)$");
for (int i = 0; i < lines.Count; i++)
{
string L = lines[i];
var m = pat.Match(L);
if (!m.Success)
{
bad.Add((i + 1, L, "не соответствует шаблону q<N>,<C>=<q|f><M>"));
continue;
}
// Источник (qN)
string src = "q" + m.Groups[1].Value;
// Символ (C)
char sym = m.Groups[2].Value[0];
// Место назначения (<q|f>M)
string dest = m.Groups[3].Value + m.Groups[4].Value;
nfa.AddTransition(src, sym, dest);
}
if (bad.Count > 0)
{
Console.WriteLine("\nОшибка в файле:");
foreach (var e in bad)
Console.WriteLine($" строка {e.Item1}: {e.Item2} -> {e.Item3}");
return;
}
nfa.states.Add(nfa.start);
Console.WriteLine("\n=== Информация ===");
Console.WriteLine("Состояния: " + string.Join(", ", nfa.states.OrderBy(x => x)));
Console.WriteLine("Начальное: " + nfa.start);
Console.WriteLine("Конечные: " + (nfa.finals.Count == 0 ? "(нет)" : string.Join(", ", nfa.finals)));
Console.WriteLine("Алфавит: " + (nfa.alphabet.Count == 0 ? "(пусто)" : string.Join(" ", nfa.alphabet.Select(c => $"'{c}'"))));
// Поиск достижимых
var reachable = new HashSet<string>();
void Dfs(string s)
{
if (reachable.Contains(s)) return;
reachable.Add(s);
if (!nfa.transitions.TryGetValue(s, out var map)) return;
foreach (var kv in map)
foreach (var d in kv.Value)
Dfs(d);
}
Dfs(nfa.start);
// Недостижимые: состояния\достжимые
var unreachable = nfa.states.Except(reachable).ToList();
// Висячие: вершины, которые не являются источниками
var dangling = nfa.states.Where(s => !nfa.transitions.ContainsKey(s)).ToList();
//var isolated = nfa.states.Where(s =>
// (!nfa.transitions.ContainsKey(s) || nfa.transitions[s].Count == 0) &&
// !nfa.transitions.Values.Any(map => map.Values.Any(list => list.Contains(s))) &&
// s != nfa.start).ToList();
if (unreachable.Count > 0)
Console.WriteLine("Недостижимые: " + string.Join(", ", unreachable));
if (dangling.Count > 0)
Console.WriteLine("Висячие вершины: " + string.Join(", ", dangling));
//if (isolated.Count > 0)
// Console.WriteLine("Изолированные вершины: " + string.Join(", ", isolated));
Console.WriteLine("\nПереходы:");
foreach (var src in nfa.transitions.OrderBy(k => k.Key))
foreach (var kv in src.Value)
foreach (var d in kv.Value)
Console.WriteLine($"{src.Key},{kv.Key}={d}");
bool isDet = nfa.IsDeterministic(out var nondet);
Console.WriteLine(isDet ? "\nАвтомат детерминирован." : "\nАвтомат НЕДЕТЕРМИНИРОВАН.");
if (!isDet)
{
Console.WriteLine("Недетерминированные переходы:");
foreach (var n in nondet)
Console.WriteLine($"{n.src},{n.sym} -> {string.Join(", ", n.dests)}");
Console.WriteLine("\n=== Детерминизация ===");
var (dfa, map) = DeterminizeWithMapping(nfa);
Console.WriteLine("Состояния ДКА:");
foreach (var kv in map)
{
string label = "{" + string.Join(",", kv.Value) + "}";
string mark = kv.Value.Any(s => s.StartsWith("f")) ? " [final]" : "";
Console.WriteLine($" q{kv.Key} => {label}{mark}");
}
Console.WriteLine("\nПереходы ДКА:");
foreach (var src in dfa.transitions.OrderBy(k => k.Key))
foreach (var kv in src.Value)
Console.WriteLine($" q{src.Key},{kv.Key}=q{kv.Value}");
Console.WriteLine($"\nНачальное: q{dfa.start}");
Console.WriteLine($"Финальные: {(dfa.finals.Count == 0 ? "(нет)" : string.Join(", ", dfa.finals.Select(i => "q" + i)))}");
WriteDotNfa(nfa, "nfa.dot");
WriteDotDfa(dfa, "dfa.dot", map);
Console.WriteLine("\nСозданы файлы nfa.dot и dfa.dot.");
Console.WriteLine("\n=== Проверка строк (Enter — завершить) ===");
while (true)
{
Console.Write("Строка: ");
string input = Console.ReadLine() ?? "";
if (string.IsNullOrEmpty(input)) break;
bool accept = dfa.Accepts(input);
Console.WriteLine(accept ? " допускается" : " НЕ допускается");
}
}
else
{
WriteDotNfa(nfa, "nfa.dot");
Console.WriteLine("\nСоздан файл nfa.dot");
Console.WriteLine("\n=== Проверка строк (Enter — завершить) ===");
while (true)
{
Console.Write("Строка: ");
string input = Console.ReadLine() ?? "";
if (string.IsNullOrEmpty(input)) break;
bool accept = nfa.AcceptsDeterministic(input);
Console.WriteLine(accept ? " допускается" : " НЕ допускается");
}
}
}
static (Dfa, Dictionary<int, HashSet<string>>) DeterminizeWithMapping(Nfa nfa)
{
var dfa = new Dfa { alphabet = new HashSet<char>(nfa.alphabet) };
var setToId = new Dictionary<string, int>();
var idToSet = new Dictionary<int, HashSet<string>>();
int nextId = 0;
var startSet = new HashSet<string> { nfa.start };
string startKey = string.Join(",", startSet.OrderBy(s => s));
setToId[startKey] = nextId;
idToSet[nextId] = startSet;
dfa.states.Add(nextId);
dfa.start = nextId;
nextId++;
var queue = new Queue<int>();
queue.Enqueue(dfa.start);
while (queue.Count > 0)
{
int curId = queue.Dequeue();
var curSet = idToSet[curId];
if (curSet.Any(s => s.StartsWith("f"))) dfa.finals.Add(curId);
foreach (char sym in dfa.alphabet)
{
var destSet = new HashSet<string>();
foreach (var s in curSet)
if (nfa.transitions.TryGetValue(s, out var map) && map.TryGetValue(sym, out var dests))
foreach (var d in dests) destSet.Add(d);
if (destSet.Count == 0) continue;
string key = string.Join(",", destSet.OrderBy(s => s));
if (!setToId.ContainsKey(key))
{
setToId[key] = nextId;
idToSet[nextId] = destSet;
dfa.states.Add(nextId);
queue.Enqueue(nextId);
nextId++;
}
int destId = setToId[key];
if (!dfa.transitions.ContainsKey(curId)) dfa.transitions[curId] = new();
dfa.transitions[curId][sym] = destId;
}
}
return (dfa, idToSet);
}
static void WriteDotNfa(Nfa nfa, string filename)
{
using var w = new StreamWriter(filename);
w.WriteLine("digraph NFA { rankdir=LR;");
foreach (var s in nfa.states)
{
string shape = s.StartsWith("f") ? "square" : "circle";
w.WriteLine($" {s} [shape={shape},label=\"{s}\"];");
}
w.WriteLine($" start [shape=point]; start -> {nfa.start};");
foreach (var src in nfa.transitions)
foreach (var kv in src.Value)
foreach (var d in kv.Value)
w.WriteLine($" {src.Key} -> {d} [label=\"{kv.Key}\"];");
w.WriteLine("}");
}
static void WriteDotDfa(Dfa dfa, string filename, Dictionary<int, HashSet<string>> map)
{
using var w = new StreamWriter(filename);
w.WriteLine("digraph DFA { rankdir=LR;");
foreach (var s in dfa.states)
{
string label = "{" + string.Join(",", map[s]) + "}";
string shape = dfa.finals.Contains(s) ? "square" : "circle";
w.WriteLine($" q{s} [shape={shape},label=\"q{s}\\n{label}\"];");
}
w.WriteLine($" start [shape=point]; start -> q{dfa.start};");
foreach (var src in dfa.transitions)
foreach (var kv in src.Value)
w.WriteLine($" q{src.Key} -> q{kv.Value} [label=\"{kv.Key}\"];");
w.WriteLine("}");
}
}
