DFA 最小化 Brzozowski 算法

发布于 2024-11-05 19:18:33 字数 299 浏览 6 评论 0原文

我正在尝试实现 Brzozowski 算法来最小化我的 DFA 以下是相同的算法。

DFA = d(r(d(r(NFA)))) 

其中,r() 是 NFA 的反转,D() 将 NFA 转换为 DFA。

但我不明白r()的含义是什么,在google上搜索也没有提供太多信息。

谁能解释一下NFA的r()是什么。

任何其他可用的简单算法或 C++ 实现请让我知道链接。

I am trying to implement Brzozowski's algorithm for minimizing my DFA
Following is the algorithm for the same.

DFA = d(r(d(r(NFA)))) 

where r() is reversal of NFA and D() converts NFA to DFA.

But I do not understand what is the meaning of r() searching on google also does not gives much information.

Can anybody please explain what is r() of NFA.

Any other simple algorithm or C++ implementation available please let me know the link.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

与君绝 2024-11-12 19:18:33

Brzozowski 的算法

Brzozowski 的算法更清晰地写为:

minimized_DFA = subset(reverse(subset(reverse(NFA))))

其中 subset 表示子集构造(也称为 动力装置构造)。子集构造通过模拟 NFA 中每个等效状态集(由于 epsilon 转换)的所有转换来构建 DFA。

反转 NFA 涉及以下步骤:

  1. 反转 NFA 中所有过渡边的方向。
  2. 创建一个新的起始状态,使 epsilon 转换到 NFA 中的所有接受状态。
  3. 在 NFA 中将所有接受状态标记为不接受。
  4. 使旧的起始状态成为新的接受状态。

步骤 2-4 有效地交换了接受状态和起始状态的角色。


Brzozowski 的算法示例

以下是基于针对编译器的 Udacity 测验 最小化 DFA 的示例当然(步骤与 NFA 作为初始输入相同)。

初始 DFA:

initial DFA

此 DFA 接受类似 {"", "a", "aa", "aaa", "aaabba", "ba", "bab", "ababa", "ababb"} 并拒绝像 {"b", "ab", "aab", "aabb", "bb", "bbb"} 这样的字符串。换句话说,它拒绝具有 "b" 的字符串,除非它们也具有 "ba" 子字符串。很明显,s1-s3s2-s4 是多余的。

第 1 步:反向 (DFA)

reverse(DFA)

步骤 2:subset(reverse(DFA))

运行子集构造以构建 DFA 表州表示每个唯一 epsilon 闭包的可能转换(^ 表示开始状态,$ 表示接受状态)

A = e-closure({s5}) = {s0,s2,s4}
B = e-closure({s0,s1,s2,s3,s4}) = {s0,s1,s2,s3,s4}
C = e-closure({s2,s4}) = {s2,s4}
D = e-closure({s1,s2,s3,s4}) = {s1,s2,s3,s4}

     a   b
-----------
A^$  B   C
B$   B   B
C    D   C
D    D   B

: net/bC85S.png" rel="nofollow noreferrer">subset(reverse(DFA))

步骤3:reverse(subset(reverse(DFA)))

反转DFA。消除公共前缀后,另一遍可以消除公共后缀。

reverse( subset(reverse(DFA)))

步骤 4:subset(reverse(subset(reverse(DFA))))

再次运行子集构造以最小化国家期货协会。

A = e-closure({E}) = {A,B}
B = e-closure({B,D}) = {B,D}
C = e-closure({A,B,C,D} = {A,B,C,D}

    a  b
--------
A^$ A  B
B   C  B
C$  C  C

subset( verse(subset(reverse(DFA))))


参考文献:

上图的 Graphviz 代码:

// initial DFA
digraph G {
    rankdir=LR;
    size="8,5"
    
    node [shape=point]; qi;
    node [shape=doublecircle]; s0 s2 s4;
    node [shape=circle];

    qi -> s0;
    s0 -> s0 [label="a"];
    s0 -> s1 [label="b"];
    s1 -> s2 [label="a"];
    s2 -> s2 [label="a"];
    s2 -> s4 [label="b"];
    s4 -> s4 [label="a,b"];
    s1 -> s3 [label="b"];
    s3 -> s3 [label="b"];
    s3 -> s2 [label="a"];
}
// reverse(DFA)
digraph G {
    rankdir=LR;
    size="8,5"
    
    node [shape=point]; qi;
    node [shape=doublecircle]; s0;
    node [shape=circle];

    qi -> s5;
    s0 -> s0 [label="a"];
    s1 -> s0 [label="b"];
    s2 -> s1 [label="a"];
    s2 -> s2 [label="a"];
    s4 -> s2 [label="b"];
    s4 -> s4 [label="a,b"];
    s3 -> s1 [label="b"];
    s3 -> s3 [label="b"];
    s2 -> s3 [label="a"];
    s5 -> s2 [label="e"];
    s5 -> s0 [label="e"];
    s5 -> s4 [label="e"];
}
// subset(reverse(DFA))
digraph G {
    rankdir=LR;
    size="8,5"
    
    node [shape=point]; qi;
    node [shape=doublecircle]; A B;
    node [shape=circle];

    qi -> A;
    A -> B [label="a"];
    A -> C [label="b"];
    B -> B [label="a,b"];
    D -> B [label="b"];
    C -> D [label="a"];
    C -> C [label="b"];
    D -> D [label="a"];
}
// reverse(subset(reverse(DFA)))
digraph G {
    rankdir=LR;
    size="8,5"
    
    node [shape=point]; qi;
    node [shape=doublecircle]; A;
    node [shape=circle];

    qi -> E;
    B -> A [label="a"];
    C -> A [label="b"];
    B -> B [label="a,b"];
    B -> D [label="b"];
    D -> C [label="a"];
    C -> C [label="b"];
    D -> D [label="a"];
    E -> A [label="e"];
    E -> B [label="e"];
}
// subset(reverse(subset(reverse(DFA))))
digraph G {
    rankdir=LR;
    size="8,5"
    
    node [shape=point]; qi;
    node [shape=doublecircle]; A C;
    node [shape=circle];

    qi -> A;
    A -> A [label="a"];
    A -> B [label="b"];
    B -> B [label="b"];
    B -> C [label="a"];
    C -> C [label="a,b"];
}

Brzozowski's algorithm

Brzozowski's algorithm is clearer written as:

minimized_DFA = subset(reverse(subset(reverse(NFA))))

where subset denotes subset construction (also known as powerset construction). Subset construction builds a DFA by simulating all transitions for each equivalent set of states (due to epsilon transitions) in the NFA.

Reversing an NFA involves these steps:

  1. Reverse the direction of all transition edges in the NFA.
  2. Create a new starting state that has epsilon transitions to all accepting states in the NFA.
  3. Mark all accepting states as non-accepting in the NFA.
  4. Make the old starting state the new accepting state.

Steps 2-4 effectively swaps the roles of accepting and starting states.


Brzozowski's algorithm example

Here's an example of minimizing a DFA based on a Udacity quiz for a compilers course (the steps are the same with an NFA as initial input).

Initial DFA:

initial DFA

This DFA accepts strings like {"", "a", "aa", "aaa", "aaabba", "ba", "bab", "ababa", "ababb"} and rejects strings like {"b", "ab", "aab", "aabb", "bb", "bbb"}. In other words, it rejects strings that have a "b" unless they also have a "ba" substring. It's pretty obvious that s1-s3 and s2-s4 are redundant.

Step 1: reverse(DFA):

reverse(DFA)

Step 2: subset(reverse(DFA)):

Run subset construction to build a table of DFA states to represent possible transitions from each unique epsilon closure (^ denotes a start state, $ denotes an accepting state):

A = e-closure({s5}) = {s0,s2,s4}
B = e-closure({s0,s1,s2,s3,s4}) = {s0,s1,s2,s3,s4}
C = e-closure({s2,s4}) = {s2,s4}
D = e-closure({s1,s2,s3,s4}) = {s1,s2,s3,s4}

     a   b
-----------
A^$  B   C
B$   B   B
C    D   C
D    D   B

subset(reverse(DFA))

Step 3: reverse(subset(reverse(DFA))):

Reverse the DFA. After eliminating common prefixes, another pass enables elimination of common suffixes.

reverse(subset(reverse(DFA)))

Step 4: subset(reverse(subset(reverse(DFA)))):

Run subset construction one more time to minimize the NFA.

A = e-closure({E}) = {A,B}
B = e-closure({B,D}) = {B,D}
C = e-closure({A,B,C,D} = {A,B,C,D}

    a  b
--------
A^$ A  B
B   C  B
C$  C  C

subset(reverse(subset(reverse(DFA))))


References:

Graphviz code for above diagrams:

// initial DFA
digraph G {
    rankdir=LR;
    size="8,5"
    
    node [shape=point]; qi;
    node [shape=doublecircle]; s0 s2 s4;
    node [shape=circle];

    qi -> s0;
    s0 -> s0 [label="a"];
    s0 -> s1 [label="b"];
    s1 -> s2 [label="a"];
    s2 -> s2 [label="a"];
    s2 -> s4 [label="b"];
    s4 -> s4 [label="a,b"];
    s1 -> s3 [label="b"];
    s3 -> s3 [label="b"];
    s3 -> s2 [label="a"];
}
// reverse(DFA)
digraph G {
    rankdir=LR;
    size="8,5"
    
    node [shape=point]; qi;
    node [shape=doublecircle]; s0;
    node [shape=circle];

    qi -> s5;
    s0 -> s0 [label="a"];
    s1 -> s0 [label="b"];
    s2 -> s1 [label="a"];
    s2 -> s2 [label="a"];
    s4 -> s2 [label="b"];
    s4 -> s4 [label="a,b"];
    s3 -> s1 [label="b"];
    s3 -> s3 [label="b"];
    s2 -> s3 [label="a"];
    s5 -> s2 [label="e"];
    s5 -> s0 [label="e"];
    s5 -> s4 [label="e"];
}
// subset(reverse(DFA))
digraph G {
    rankdir=LR;
    size="8,5"
    
    node [shape=point]; qi;
    node [shape=doublecircle]; A B;
    node [shape=circle];

    qi -> A;
    A -> B [label="a"];
    A -> C [label="b"];
    B -> B [label="a,b"];
    D -> B [label="b"];
    C -> D [label="a"];
    C -> C [label="b"];
    D -> D [label="a"];
}
// reverse(subset(reverse(DFA)))
digraph G {
    rankdir=LR;
    size="8,5"
    
    node [shape=point]; qi;
    node [shape=doublecircle]; A;
    node [shape=circle];

    qi -> E;
    B -> A [label="a"];
    C -> A [label="b"];
    B -> B [label="a,b"];
    B -> D [label="b"];
    D -> C [label="a"];
    C -> C [label="b"];
    D -> D [label="a"];
    E -> A [label="e"];
    E -> B [label="e"];
}
// subset(reverse(subset(reverse(DFA))))
digraph G {
    rankdir=LR;
    size="8,5"
    
    node [shape=point]; qi;
    node [shape=doublecircle]; A C;
    node [shape=circle];

    qi -> A;
    A -> A [label="a"];
    A -> B [label="b"];
    B -> B [label="b"];
    B -> C [label="a"];
    C -> C [label="a,b"];
}
恰似旧人归 2024-11-12 19:18:33

这是 OpenFst 的实现。

这篇论文中,有一个图表(第15页)显示了以下结果:应用相反的操作。

帮助理解 FSM 操作的一种更简单的方法是使用 OpenFst 等库来创建和操作机器,然后使用 Graphviz 将结果可视化。

This is the implementation from OpenFst.

In this paper is a diagram (page 15) that show results of applying the reverse operation.

An easier way to help understand FSM operations is to use a library like OpenFst to create and manipulable the machines and then visualize the results using Graphviz.

戈亓 2024-11-12 19:18:33

在reverse.c的代码中(找到这里,但现在已不存在)您会发现注释/* Create reversed Edges */。所以我想说 r() 正在反转所有边的方向(另外还要确保反转的自动机具有明确定义的开始状态)。

In the code for reverse.c (found here, but now defunct) you'll find a comment /* Create reversed edges */. So I'd say that r() is reversing the direction of all edges (plus making sure that the reversed automaton has a well defined start state).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文