[英]Algorithm for extracting a language from DFA
我正在尝试找到一种从DFA中提取语言的算法。 如果该语言是无限的,那么我只需要报告: maxcount
<= 1000个字符串。 对报告的顺序没有限制。
我试过:(我不知道如何编写算法,所以我只是用文字解释我做了什么)
递归算法 - 对于从一个状态到下一个状态/状态的每个转换,保存转换字符,然后对每个转换进行递归调用,如果转换到接受状态然后报告。如果我们已达到“死胡同”然后有注意到。
假设开始状态是state 1
并且有两个转换到: state 2
和state 3
具有转换字符'a'
, 'b'
。 然后是从state 3
到state 4
的转换,转换字符'c'
,只有state 4
是接受状态。
从state 1
到state 2
将保存'a'
作为转换字符,但由于state 2
是死胡同,因此它将注意到,因为它不是接受状态,所以有记录报告。
从state 1
到state 3
将保存'b'
然后从state 3
转到state 4
将保存'c'
以便我们总共具有"bc"
并且因为state 4
没有任何转换,所以递归结束。 我们报告"bc"
因为state 4
是接受状态
如果我有一个带循环的DFA - 那么为了不在该循环中“卡住”,我确保“如果”还有另一个可能的转换,那么我们将总是选择那个转换,而不是我们上次做出的转换/我们处于那种状态的时间(我们在哪些状态下进行过渡的记忆)
该算法适用于小型DFA,但它会在更大的DFA上产生堆栈溢出:s(想象20从state 1
转换到state 2
和从state 2
转换到state 3
,依此类推)。
任何人都可以推荐更高效的算法吗?
交替:
我会通过广泛优先搜索DFA来执行此操作,这将生成按长度排序的字符串。
定义一个由状态和字符串组成的对象(这里有一个更节省内存的解决方案,但我认为如果你只需要生成1000个字符串,这样就可以了)。 然后创建此类对象的工作队列。 使用单个对象初始化工作队列,该对象的状态为起始状态且其字符串为空。
现在重复以下三个步骤,直到找到maxcount
字符串或队列变为空:
删除队列中的第一个对象。
如果其状态是接受状态,则输出其字符串。
对于每个可能的out转换(由一个字符和一个新状态组成),将一个新对象添加到队列的末尾,该对象具有转换的状态以及对象的字符串与转换字符的串联。
如果使用BFS,则循环无关紧要。 我们的想法是定义包含当前状态的搜索节点和指向前任节点的指针。 因此,当您访问节点以进行接受状态时,可以向后跟踪先前的指针以确定接受的字符串(反之)。 事实证明,如果搜索节点还包含导致从前一个节点的状态转换到当前节点的字符,那么它的优雅。
这是Java中的一种方法:
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
class Experimental {
// DFA state with its transitions, possibly accepting.
static class State {
final Map<Character, State> transitions = new HashMap<>();
final boolean accept;
State(boolean accept) {
this.accept = accept;
}
}
// A little DFA.
static final State s0 = new State(false);
static final State s1 = new State(false);
static final State s2 = new State(true);
static final State s3 = new State(true);
static {
s0.transitions.put('a', s1);
s0.transitions.put('b', s2);
s0.transitions.put('c', s3);
s1.transitions.put('d', s3);
s2.transitions.put('e', s0);
s2.transitions.put('f', s1);
}
// An enumerator of strings accepted by the DFA in order of length.
static class Enumerator {
static class Node {
final Node prev;
final char prevCh;
final State state;
Node(State start) {
this(null, Character.MIN_VALUE, start);
}
Node(Node prev, char ch, State state) {
this.prev = prev;
this.prevCh = ch;
this.state = state;
}
}
final Deque<Node> queue = new ArrayDeque<>();
final List<String> output = new ArrayList<>();
final State start;
Enumerator(State start) {
this.start = start;
}
Enumerator enumerate(int outputLimit) {
queue.clear();
output.clear();
// Enqueue a search node for the start state.
queue.add(new Node(start));
while (!queue.isEmpty() && output.size() < outputLimit) {
Node current = queue.pollFirst();
if (current.state.accept) {
// Follow prev pointers to build the accepted string.
StringBuilder sb = new StringBuilder();
for (Node p = current; p.prev != null; p = p.prev) {
sb.append(p.prevCh);
}
output.add(sb.reverse().toString());
}
// Enqueue nodes for the successors of current state.
for (Entry<Character, State> transition : current.state.transitions.entrySet()) {
queue.addLast(new Node(current, transition.getKey(), transition.getValue()));
}
}
return this;
}
}
public static void main(String[] args) {
System.out.println(new Enumerator(s0).enumerate(20).output);
}
}
输出:
[b, c, ad, beb, bec, bfd, bead, bebeb, bebec, bebfd, bebead, bebebeb, bebebec, bebebfd, bebebead, bebebebeb, bebebebec, bebebebfd, bebebebead, bebebebebeb]
注意 :我猜您可以将此问题建模为查找开始状态和所有接受状态之间的所有路径。 所以,我相信这可以通过深度优先搜索图来完成。 深度优先搜索将找到两个节点之间的所有非循环路径。
如果您熟悉深度优先搜索(DFS)算法,这是一个图搜索/遍历算法可以帮助您解决问题。 让我举一个非常简单的例子。
给定有向图,源顶点'和目标顶点'd',打印从给定's'到'd'的所有路径。 请考虑以下有向图。 设s为2,d为3.有4个不同的路径,从2到3。
我们的想法是对给定的有向图进行Depth First Traversal
。 从源开始遍历。 继续将访问过的顶点存储在数组中,如path[]
。 如果我们到达目标顶点,则打印path[]
内容。 重要的是将path[]
当前顶点标记为也访问,以便遍历不会进入循环。
示例代码 :您可以在此处找到一个非常简单的Java
代码,以查找图中两个节点之间的所有路径。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.