我无法想出一个成功接受以下内容的合适自动机:
当然任何数量的{a,b,c} *组成的字符串,但是
n(a) + n(b) != n(c)
最初,我已经将'x'分配给堆栈中的任何'a'或'b',并在遇到'c'时从堆栈中删除'x'。这将根据需要在两个状态之间循环。当然,在任何数量的c在a和b之前的情况下,这很复杂。我显然无法从空堆栈中删除。欢迎任何想法或建议!
答案 0 :(得分:1)
这台PDA并非无足轻重。你必须使用两个分支(一个用于增量(A和B),一个用于减量(C))。诀窍是放置一个堆栈顶部标记并寻找这个标记。如果你按住它(不要在你的乐队上更进一步)切换分支并推动你的分支上的负片。
在下文中,我通过三重定义转换:(输入,弹出,推送)
例如:
对于你的例子:
S = starting state
Q1 = "switch branches" state, from here the stack is empty and we move in the branch we need in dependend of the next character read from the input band.
Q2, Q3 = States of the there-is-an-A-or-B-next branch
Q4, Q5 = States of the there-is-a-C-next branch
Q6 = State of acceptance (the only one. all other states are not accepting)
现在转换:
S -(ϵ,ϵ,#)-> Q1 ' # is our top-of-the-stack-marker
' first branch (for more As and Bs)
Q1 -(AB,ϵ,+)-> Q2 ' for every A or B push a + sign on the stack
Q2 -(AB,ϵ,+)-> Q2
Q2 -(C,+,ϵ)-> Q3 ' for every C consume a + sign
Q3 -(AB,ϵ,+)-> Q2
Q3 -(C,+,ϵ)-> Q3
Q3 -(ϵ,#,#)-> Q1 ' if we reach the top of the stack, return to the Q1 state
Q2 -(ϵ,#,#)-> Q1
Q2 -($,+,+)-> Q6 ' if the input is processed and the stack is not empty => go in the state of acceptance
Q3 -($,+,+)-> Q6 ' if the input is processed and the stack is not empty => go in the state of acceptance
' second branch (for more Cs)
Q1 -(C,ϵ,-)-> Q4 ' for every C push a - sign on the stack
Q4 -(C,ϵ,-)-> Q4
Q4 -(AB,-,ϵ)-> Q5 ' for every A or B consume a - sign
Q5 -(AB,-,ϵ)-> Q5
Q5 -(C,ϵ,-)-> Q4
Q5 -(ϵ,#,#)-> Q1
Q4 -(ϵ,#,#)-> Q1
Q4 -($,-,-)-> Q6 ' if the input is processed and the stack is not empty => go in the state of acceptance
Q5 -($,-,-)-> Q6 ' if the input is processed and the stack is not empty => go in the state of acceptance
逻辑描述:
我们在堆栈上按#-sign来检测堆栈的顶部。 现在我们在两个“区域”运营。与Cs相比,第一个区域处理A和B的正数,而另一个区域处理与Cs相比的As和B的负数。
我们正在处理作为Stack的一部分的三个标志:
第一个转换是从开始状态到新的开始状态。此转换仅用于推动堆栈顶部标记的原因。永远不会再达到新的原始起始状态。 新的起始状态是开关,每当我们触及堆栈顶部标记时就会命中。从这个状态开始,我们将根据下一个A和B或C访问这些区域。
我希望能够解释它。描述的自动机应该给你一个好主意。我认为它有一点错误,但是如果你按照这个想法做到了,那么你可以解决它:)
我用在线模拟器构建描述的自动机来测试它。你可以找到它here。这个模拟器使用epsilon作为输入频段的初始化并且什么都不做......所以它有点令人困惑。我添加了一些可以通过单击“批量测试”旁边的绿色箭头运行的测试。
答案 1 :(得分:1)
我们可以尝试在某种程度上简化问题。你想要n(a) + n(b) != n(c)
。在没有其他限制的情况下,这意味着就PDA而言a
和b
是无法区分的;所以这个语言与n(a) != n(c)
相同(如果我们让PDA假装b
是a
s)。如果我们可以为这种简单的语言制作PDA,我们就完成了。
我们可能会问这个目标是确定性的还是任何PDA。并非所有无上下文的语言都具有确定性PDA,因此,一般而言,PDA指的是非确定性的PDA。如果我们假设这个问题很好,我们可以通过写下等效条件n(a) != n(c)
来进一步简化条件n(a) < n(c) or n(a) > n(c)
。这使我们的生活变得更加轻松,因为现在我们只需要为n(a) < n(c)
提出一个PDA,然后我们就完成了:我们语言的PDA将不确定地分支到两个PDA,每个方向一个
那么我们如何获得n(a) < n(c)
的PDA?好吧,我们可以从n(a) = n(c)
的PDA开始,而不是接受没有输入和空堆栈,接受没有输入和堆栈表明c
比a
更多n(a) = n(c)
秒。用于n(c) - n(a)
的PDA是什么样的?我们需要一个战略。诀窍是使用堆栈来跟踪运行差异n(c) - n(a) = 0
,如下所示:
Z
,则堆栈为空,最顶部的符号为n(c) - n(a) > 0
,即堆栈符号的底部。n(c) - n(a)
,则堆栈包含c
的{{1}}个实例。n(c) - n(a) < 0
,则堆栈包含n(a) - n(c)
的{{1}}个实例。要接受a
,我们会先阅读n(a) = n(c)
和a
s并消耗输入。然后,我们看到堆栈顶部有什么:
c
(请参阅上面的第一个项目符号)n(a) = n(c)
,c
(请参阅上面的第二个项目符号)n(a) < n(c)
,a
(请参阅上面的第三个项目符号)要接受n(a) > n(c)
,只要我们在堆栈顶部看到堆栈底部符号n(a) = n(c)
,我们就会以非确定方式转换为新的接受状态q
。
要接受Z
,只要我们在堆栈顶部看到n(a) < n(c)
,我们就会非确定地转换为新的接受状态q
。在这种情况下,如果要接受空堆栈,则可以清除处于接受状态的堆栈。
要接受c
,只要我们在堆栈顶部看到n(a) > n(c)
,我们就会非确定地转换为新的接受状态q
。在这种情况下,如果要接受空堆栈,则可以清除处于接受状态的堆栈。
观察我们可以一起处理a
和n(a) < n(c)
;我们甚至不需要在前面进行不确定性的分支。这是刚刚描述的PDA的示例实现:
n(a) > n(c)
您需要像Q x s Q' s'
///////////////////////
// count a //
///////////////////////
q0 a Z q0 aZ
q0 a a q0 aa
q0 a c q0 e
///////////////////////
// count c //
///////////////////////
q0 c Z q0 cZ
q0 c a q0 e
q0 c c q0 cc
///////////////////////
// move to accepting //
///////////////////////
q0 e a q a
q0 e c q c
///////////////////////
// clear stack //
///////////////////////
q e a q e
q e c q e
一样为b
添加转换规则,但a
与b
无法区分,应该这样对待。< / p>
上表中的列如下:
a
:转换源自的状态Q
:导致转换的输入字母表符号,如果转换不消耗输入,则为空字符串x
e
:最顶层的堆栈符号s
:转换终止的状态Q'
:在转换例如,行
s'
编码转换,可以描述如下:
从堆栈底部符号
q0 a Z q0 aZ
的状态q0
开始,使用输入符号Z
并转换到状态a
,推送{{ 1}}位于q0
之上。