B={0n1n|n≥0} B = { 0 n 1 n | n ≥ 0 }
- Michael Sipser 的 Introduction to the Theory of Computation 的1.4提到一个非正则语言的例子, B={0n1n|n≥0} B = { 0 n 1 n | n ≥ 0 } ,并用Pumping lemma证明了其是非正则的,无法被DFA识别
- 但是我们的计算机似乎可以识别以上语言,所以,为什么我们的计算机还是DFA呢
- 事实上,我们的计算机只能识别n处于一定范围的B,而不能识别n为任意值的B。如果n过大,我们的计算机的存储单元无法表达,则会发生溢出,导致识别了另一个语言 C={0n1m|n mod max=m mod max} C = { 0 n 1 m | n m o d m a x = m m o d m a x } ,其中,max是我们的计算机的存储单元可以表达的最大值
- 如果是n处于一定范围内,容易构造出一台DFA识别B:
- 设有两个变量a、b,a、b都可以取值 [0,1000] [ 0 , 1000 ] ,a表示0的数量,b表示1的数量
- 那么,a、b的所有笛卡尔积组成的集合的一个子集就是该DFA的状态集
- 转移方程为
- 首先: <0,0>→<1,0>→<2,0>→...→<1000,0> < 0 , 0 >→< 1 , 0 >→< 2 , 0 >→ . . . →< 1000 , 0 > <script type="math/tex" id="MathJax-Element-44"><0,0>\rightarrow<1,0>\rightarrow<2,0>\rightarrow...\rightarrow<1000,0></script>
- 然后:从任意 <n,0> < n , 0 > <script type="math/tex" id="MathJax-Element-45"> </script> 都可以转移到 <n,1> < n , 1 > <script type="math/tex" id="MathJax-Element-46"> </script>
- 然后:一旦转移到 <n,1> < n , 1 > <script type="math/tex" id="MathJax-Element-47"> </script> ,就不能转移到 <n+k,m>,(k>0) < n + k , m > , ( k > 0 ) <script type="math/tex" id="MathJax-Element-48"> , (k>0)</script> ,也就是不能增长n的值
- 最后:一旦转移到 <n,n> < n , n > <script type="math/tex" id="MathJax-Element-49"> </script> 就accept
为什么现实中的计算机有可能是DFA
- 计算机只有有限存储,such as,某计算机有 232 2 32 个8bit的cell,每个cell有 28 2 8 种取值,所以,所有存储的取值有 (28)232 ( 2 8 ) 2 32 种,也就是,这台计算机的存储只能有 (28)232 ( 2 8 ) 2 32 种状态,这就使得把计算机转为DFA变为可能
对任意一段代码构造等价的NFA
NFA构造方法描述
假设该段代码的运行过程会改变的存储cell有c1、c2、c3、c4、c5,其中,c1到c5的取值范围都是[0,0xFF],那么就可以构造 (28)5 ( 2 8 ) 5 个 <c1,c2,c3,c4,c5> < c 1 , c 2 , c 3 , c 4 , c 5 > <script type="math/tex" id="MathJax-Element-116"> </script> ,作为该NFA的状态集合
输入字符表是 {0,1} { 0 , 1 } ,将所有计算机接受的输入全部编码成bit 串后读入该DFA,每次读一个0或者一个1
假设该代码起始时,c1到c5的取值为 0,0,0,0,0 0 , 0 , 0 , 0 , 0 ,则 <0,0,0,0,0> < 0 , 0 , 0 , 0 , 0 > <script type="math/tex" id="MathJax-Element-119"><0,0,0,0,0></script> 就是起始态
- 假设该段代码处于accept状态时,c1到c5的取值分别为 1,2,3,4,5 1 , 2 , 3 , 4 , 5 ,那么, <1,2,3,4,5> < 1 , 2 , 3 , 4 , 5 > <script type="math/tex" id="MathJax-Element-121"><1,2,3,4,5></script> 就是该NFA的accept state
- 该代码可以有输入,也可以无输入,可以在多个时候输入,也可以只在一个时候输入。假设其某次输入处于 <d1,d2,d3,d4,d5> < d 1 , d 2 , d 3 , d 4 , d 5 > <script type="math/tex" id="MathJax-Element-122"> </script> ,输入后处于 <d6,d7,d8,d9,d10> < d 6 , d 7 , d 8 , d 9 , d 10 > <script type="math/tex" id="MathJax-Element-123"> </script> ,则在从 <d1,d2,d3,d4,d5> < d 1 , d 2 , d 3 , d 4 , d 5 > <script type="math/tex" id="MathJax-Element-124"> </script> 到 <d6,d7,d8,d9,d10> < d 6 , d 7 , d 8 , d 9 , d 10 > <script type="math/tex" id="MathJax-Element-125"> </script> 是一个转移,转移使用的字符就是读入的字符。该代码也可以在无输入情况下由cpu作用,从一个状态转移到另一个状态,则在这两个状态之间使用 ε ε
为什么该NFA只接受允许的字符串,而不会接受多余的字符串
- 首先,可以使用以上方法对于每个接受的字符串都构造NFA,则该NFA只接受允许的那个字符串
- 设置一个新的起始态,该起始态有 ε ε 到所有为每个字符串构造的NFA
- 则最终的NFA只接受允许的字符串
是否会出现情况:代码两次处于某状态但是行为不一样
- 答案是肯定不会
- 计算机的行为由当前状态决定——下一步要执行什么代码,取决于正在被执行的代码段,以及ip寄存器的值,如果text段的内容相同,cs:ip寄存器的相同,指令使用的值也相同,那么下一步的行为就是确定的
对任意一台计算机构造等价的DFA
- 对于一台确定的计算机可以构造出确定的DFA,执行该计算机可以做的任何计算
- 先说明一些前置情况:
- 现实中计算机使用源码处理特定的输入
- 把现实中计算机处理该输入的源码编码成确定长度(统一取最大长度M——M是该计算机可以存储的最长比特串长度,比如 240 2 40 比特)的字符串,每个字符表示一个指令——这个其实等价于把源码编译成机器码(每个机器指令由一样的长度的比特串表示),然后把这个比特串补全成统一长度M
- 因为源码是M比特长,所以最多有 2M 2 M 种不同的源码,根据上文,可以为每种源码构造一个DFA
- 然后开始构造DFA
- 该DFA分为两个部分:第一个部分解释输入的源码字符串,第二个部分实际运行输入的源码来跑输入的数据——即实际运行 2M 2 M 个不同的DFA中的一个
- DFA第一个部分,其读入长度M的字符串(也就是源码),从而把该台DFA导向某个状态,这个状态是 2M 2 M 个实际运行代码的DFA中的一个的起始态
- 第二部分就是那 2M 2 M 台DFA,其接受输入,并最终停在某个状态——accept态或者是其他状态
- 从而该DFA接受 ∪Ai ∪ A i ,其中 Ai A i 是该计算机可以运行的任意一段代码接受的字符串集合
计算机可以死循环,DFA不会死循环,为什么
consider that,死循环指的是无输入情况下,不停机,而不是无限输入导致的不停机。
因为计算机的状态是有限的,所以,如果计算机死循环,那么其必然会重复处于某个状态,进而导致行为的重复——因为计算机的当前状态唯一确定了以后的行为。
在NFA,这种情况死循环的状况也会发生——一组状态以 ε ε 连接在一起形成环,从而导致无限读入 ε ε ,无限循环
截断导致NFA死循环的那个环,并不会影响接受的语言。假设该环是s1、s2、s3、s1,那么我们可以把其变成三条路:“s1、s2、s3”,“s2、s3、s1”,“s3、s1、s2” ,非形式化来说,因为这不会改变该图的连通性,所以接受的字符串集合不变
而按照Introduction to the Theory of Computation 的1.2节的*EQUIVALENCE OF NFAS AND DFAS*DFA 描述的方法,其正是去掉了这种 ε ε 环,所以不会死循环,具体去掉的方法如下(截图来自书本英文第三版P56):
E(R) E ( R ) 是一个集合,集合中元素不重复,而 ε ε 环所到达的状态必然是重复的,所以就把 ε ε 环断开了