У меня вопрос о минимизации DFA. Поэтому я использовал очень хорошо известные методы для преобразования регулярного выражения в NFA, а затем построил из него DFA, используя алгоритм goto/clos. Теперь вопрос в том, как свести его к минимуму? Я смотрел лекции об этом здесь: http://www.youtube.com/watch?v=T9Z66NF5YRk, и я до сих пор не могу понять. Что такое минимизация DFA? Является ли это просто слиянием ИДЕНТИКАЛЬНЫХ состояний (состояний, которые поступают в одни и те же состояния на одни и те же символы) или что-то другое?DFA minimization
Итак, я начал со следующей грамматикой:
%digit = '0'..'9'
%letter = 'a'..'z' | 'A'..'Z'
%exponent = ("e" | "E") ("+" | "-")? digit+
T_INT = digit+
T_FLOAT = T_INT exponent
T_IDENTIFIER = (letter | "$" | "_") (letter | "$" | "_" | digit)*
и в конечном итоге со следующим DFA (представлено в виде JSON):
{
"START": [{
"type": "range",
"from": 36,
"to": 36,
"shift": "1"
}, {
"type": "range",
"from": 48,
"to": 57,
"shift": "2"
}, {
"type": "range",
"from": 65,
"to": 90,
"shift": "1"
}, {
"type": "range",
"from": 95,
"to": 95,
"shift": "1"
}, {
"type": "range",
"from": 97,
"to": 122,
"shift": "1"
}],
"1": [{
"type": "range",
"from": 36,
"to": 36,
"shift": "1"
}, {
"type": "range",
"from": 48,
"to": 57,
"shift": "1"
}, {
"type": "range",
"from": 65,
"to": 90,
"shift": "1"
}, {
"type": "range",
"from": 95,
"to": 95,
"shift": "1"
}, {
"type": "range",
"from": 97,
"to": 122,
"shift": "1"
}, {
"shift": ["t_identifier"]
}],
"2": [{
"type": "range",
"from": 48,
"to": 57,
"shift": "2"
}, {
"type": "range",
"from": 69,
"to": 69,
"shift": "3"
}, {
"type": "range",
"from": 101,
"to": 101,
"shift": "3"
}, {
"shift": ["t_int"]
}],
"3": [{
"type": "range",
"from": 43,
"to": 43,
"shift": "5"
}, {
"type": "range",
"from": 45,
"to": 45,
"shift": "5"
}, {
"type": "range",
"from": 48,
"to": 57,
"shift": "4"
}],
"4": [{
"type": "range",
"from": 48,
"to": 57,
"shift": "4"
}, {
"shift": ["t_float"]
}],
"5": [{
"type": "range",
"from": 48,
"to": 57,
"shift": "4"
}]
}
Так как же я минимизировать это?
UPDATE:
Хорошо, так вот мой алгоритм. Учитывая следующие DFA:
{
0: [{
from: 97,
to: 97,
shift: 1
}],
1: [{
from: 97,
to: 97,
shift: 3
}, {
from: 98,
to: 98,
shift: 2
}],
2: [{
from: 98,
to: 98,
shift: 4
}],
3: [{
from: 97,
to: 97,
shift: 3
}, {
from: 98,
to: 98,
shift: 4
}],
4: [{
from: 98,
to: 98,
shift: 4
}]
}
Это то, что я делаю, чтобы свести его к минимуму:
Для каждого состояния (пронумерованных как 0, 1, 2, 3, 4 в моем примере) получить уникальный хэш идентифицирует такое состояние (например, для состояния 0 это будет: от = 97, до = 97, shift = 1, для состояния 1 это будет: от = 97, до = 97, shift = 3 & от = 98, до = 98, shift = 2 и т. д.)
Сравните полученные хэши, и если мы найдем два одинаковых, то слейте их. В моем примере хеш состояния2 будет: от = 98 & shift = 4 & to = 98, а состояние hash будет: от = 98 & shift = 4 & to = 98, они одинаковы, поэтому мы можем объединить их в новый state5, после чего DFA будет выглядеть следующим образом:
{ 0: [{ from: 97, to: 97, shift: 1 }], 1: [{ from: 97, to: 97, shift: 3 }, { from: 98, to: 98, shift: 5 }], 3: [{ from: 97, to: 97, shift: 3 }, { from: 98, to: 98, shift: 5 }], 5: [{ from: 98, to: 98, shift: 5 }]
}
Продолжайте «, пока мы не получим никаких изменений, так что следующий шаг (слияние состояний 1 и 3) преобразует DFA в нижеследующая форма:
{ 0: [{ from: 97, to: 97, shift: 6 }], 6: [{ from: 97, to: 97, shift: 6 }, { from: 98, to: 98, shift: 5 }], 5: [{ from: 98, to: 98, shift: 5 }]
}
Нет более идентичных состояний, это означает, что мы закончили.
ВТОРАЯ UPDATE:
Итак, учитывая следующее регулярное выражение: 'а' ('се') * ('d' | 'ха' | 'AFE') + | 'b' ('ce') * ('d' | 'aa' | 'AFe') + 'ce' + У меня есть следующее DFA (START -> start state, ["accept"] -> so to например, переход в принимающее состояние):
{
"START": [{
"type": "range",
"from": 98,
"to": 98,
"shift": "1.2"
}, {
"type": "range",
"from": 97,
"to": 97,
"shift": "17.18"
}],
"1.2": [{
"type": "range",
"from": 120,
"to": 120,
"shift": "10"
}, {
"type": "range",
"from": 100,
"to": 100,
"shift": "6.7"
}, {
"type": "range",
"from": 65,
"to": 65,
"shift": "8"
}, {
"type": "range",
"from": 99,
"to": 99,
"shift": "4"
}],
"10": [{
"type": "range",
"from": 97,
"to": 97,
"shift": "6.7"
}],
"6.7": [{
"type": "range",
"from": 99,
"to": 99,
"shift": "15"
}, {
"type": "range",
"from": 120,
"to": 120,
"shift": "13"
}, {
"type": "range",
"from": 100,
"to": 100,
"shift": "6.7"
}, {
"type": "range",
"from": 65,
"to": 65,
"shift": "11"
}],
"15": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "14.accept"
}],
"14.accept": [{
"type": "range",
"from": 99,
"to": 99,
"shift": "16"
}, {
"shift": ["accept"]
}],
"16": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "14.accept"
}],
"13": [{
"type": "range",
"from": 97,
"to": 97,
"shift": "6.7"
}],
"11": [{
"type": "range",
"from": 70,
"to": 70,
"shift": "12"
}],
"12": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "6.7"
}],
"8": [{
"type": "range",
"from": 70,
"to": 70,
"shift": "9"
}],
"9": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "6.7"
}],
"4": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "2.3"
}],
"2.3": [{
"type": "range",
"from": 120,
"to": 120,
"shift": "10"
}, {
"type": "range",
"from": 100,
"to": 100,
"shift": "6.7"
}, {
"type": "range",
"from": 65,
"to": 65,
"shift": "8"
}, {
"type": "range",
"from": 99,
"to": 99,
"shift": "5"
}],
"5": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "2.3"
}],
"17.18": [{
"type": "range",
"from": 120,
"to": 120,
"shift": "25"
}, {
"type": "range",
"from": 100,
"to": 100,
"shift": "22.accept"
}, {
"type": "range",
"from": 65,
"to": 65,
"shift": "23"
}, {
"type": "range",
"from": 99,
"to": 99,
"shift": "20"
}],
"25": [{
"type": "range",
"from": 97,
"to": 97,
"shift": "22.accept"
}],
"22.accept": [{
"type": "range",
"from": 120,
"to": 120,
"shift": "28"
}, {
"type": "range",
"from": 100,
"to": 100,
"shift": "22.accept"
}, {
"type": "range",
"from": 65,
"to": 65,
"shift": "26"
}, {
"shift": ["accept"]
}],
"28": [{
"type": "range",
"from": 97,
"to": 97,
"shift": "22.accept"
}],
"26": [{
"type": "range",
"from": 70,
"to": 70,
"shift": "27"
}],
"27": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "22.accept"
}],
"23": [{
"type": "range",
"from": 70,
"to": 70,
"shift": "24"
}],
"24": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "22.accept"
}],
"20": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "18.19"
}],
"18.19": [{
"type": "range",
"from": 120,
"to": 120,
"shift": "25"
}, {
"type": "range",
"from": 100,
"to": 100,
"shift": "22.accept"
}, {
"type": "range",
"from": 65,
"to": 65,
"shift": "23"
}, {
"type": "range",
"from": 99,
"to": 99,
"shift": "21"
}],
"21": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "18.19"
}]
}
История такая же, как ее минимизировать?Если я следую классическому алгоритму Хопкрофта со всей этой конструкцией таблицы, определяя неразличимые состояния, объединяя их вместе и так далее, тогда я собираюсь получить DFA, который содержит 15 состояний (используйте этот инструмент: http://regexvisualizer.apphb.com/ с этим регулярным выражением a (ce) (d | xa | AFe) + | b (ce) (d | xa | AFe) + ce +, чтобы проверить это). Вот как DFA выглядит после минификация с алгоритмом Hopcroft по:
Алгоритм, который я придумал, после того, как «переосмысление» алгоритма Hopcroft, в строит DFA, который меньше, чем тот, который вы можете увидеть выше (извините для качества изображения, мне пришлось перерисовывать его шаг за шагом, чтобы понять, почему это меньше):
А вот как это работает, решение о «государственной эквивалентности» на основе результата хэш функция, которой задано состояние (например, «СТАРТ») b uilds короткие строки, которые могут быть построены из DFA, если мы начнем с этого состояния. Учитывая DFA выше и состояние START, мы можем построить следующие строки: 98-> 120, 98-> 100, 98-> 65, 98-> 99, 97-> 120, 97-> 100, 97-> 65 , 97-> 99, поэтому пусть это будет результат хэш-функции для состояния START. Если мы запустим эту функцию для каждого состояния в DFA, мы увидим, что для некоторых состояний эта функция дает нам одинаковые результаты («1,2», «6,7», «2,3» и «10», «13» и «15», , «16» и «11», «8», «26», «23» и «12», «9», «4», «5», «20», «21» AND «17.18», 18.19 "и" 25 "," 28 "и" 27 "," 24 "), поэтому все, что нам нужно сделать, это объединить эти состояния вместе.
Я вижу, что я где-то не прав, но не понимаю, что случилось с минимизированным DFA, созданным моим алгоритмом?
Там также обсуждается здесь: http://binarysculpting.com/2012/03/21/dfa-state-minimization/, поэтому я надеюсь, что в конце концов получить ответ откуда-то ... – Ruslan
Спасибо всем, мои попытки найти более элегантное решение для минимизации DFA не удалось, поэтому я, наконец, реализовал классический Hopcroft's, и я доволен это :) – Ruslan