模拟NFA


15

非确定性有限自动机是一个有限状态机,其中一个元组被映射到多个状态。就是 我们用另一个函数Δ Q × Σ PQ 代替DFA的常规δ Q × Σ Q转换函数。(state,symbol)δ:Q×ΣQ Δ:Q×ΣP(Q)

如果您知道什么是NFA,则可能要跳过下一部分。

正式定义

NFA的唯一描述是

  • 有限状态集Q
  • 一组有限的符号Σ
  • 跃迁函数Δ:Q×ΣP(Q)
  • 的初始状态q0Q
  • 一组最终状态FQ

该机在开出和读取符号的有限串w ^ ∈ Σ *,每个符号将同时与当前状态应用转换函数功能,每一个新的状态集添加到组当前状态。q0wΣ

挑战

对于此挑战,我们将忽略来简化它,此外,对于某些非负整数N,字母始终是(小写的)字母az,状态集将是{ 0 N }。初始状态将始终为0F a z {0N}N0

给定一个词和NFA的描述,你的任务是确定所有的最终状态。w{az}

考虑字符串和以下描述:abaab

state, symbol, new-states
0, 'a', [1]
1, 'a', [0]
1, 'b', [1,2]

机器将从q0=0

  1. :新状态{ 1 }a{1}
  2. 读取:新的状态{ 1 2 }b{1,2}
  3. 读取:新状态{ 0 }a{0}
  4. :新状态{ 1 }a{1}
  5. 读取:新的状态{ 1 2 }b{1,2}

因此,最终状态,因此输出将是{1,2}

注意:在步骤(2)中,状态映射映射到∅,因为描述仅包括到非空集的转换。2

规则

输入将包含一个字符串和NFA的某种描述(无 -transitions):ϵ

  • 输入字符串将始终是{ az } ∗的元素{az}
  • 有效输入(不限于):
    • 元组/列表的列表/数组
    • 新行分隔输入
  • NFA的说明将仅包含带有非空集的过渡
    • 如果结果相同,则可以使用相同字符的规则缩写(例如,规则,0,'a',[1,2]并且0,'b',[1,2]可以缩写为0,"ab",[1,2]
    • 您可以将每个规则分开(例如rule 0,'a',[1,2]可以是0,'a',[1]0,'a',[2]
  • 您可以根据需要选择大写字母
  • 您可以将状态数作为输入
  • 您可以假设输入的某种排序(例如,按状态或符号排序)

输出将是最终状态的列表/集合/换行分隔的输出等。

  • 顺序没关系
  • 没有重复(因为它是一组)

测试用例

这些示例的格式为description word -> stateswhere description是元组列表(state,symbol,new-states)

[]  "x" -> []
[]  "" -> [0]
[(0,'a',[1]),(1,'a',[0]),(1,'b',[1,2])]  "abaab" -> [1,2]
[(0,'a',[1]),(1,'a',[0]),(1,'b',[1,2])]  "abc" -> []
[(0,'p',[0,1]),(0,'g',[2]),(1,'c',[1]),(1,'g',[4]),(1,'p',[2]),(2,'c',[0])]  "ppcg" -> [2,4]
[(0,'f',[1]),(1,'o',[1,2]),(2,'b',[3]),(3,'a',[4]),(4,'r',[0,4])]  "foobar" -> [0,4]
[(0,'f',[1]),(1,'o',[1,2]),(2,'b',[3]),(3,'a',[4]),(4,'r',[0,4])]  "fooooooobar" -> [0,4]
[(0,'f',[1]),(1,'o',[1,2]),(2,'b',[3]),(3,'a',[4]),(4,'r',[0,4])]  "fobarfo" -> [1,2]
[(0,'f',[1]),(1,'o',[1,2]),(2,'b',[3]),(3,'a',[4]),(4,'r',[0,4])]  "foobarrf" -> [1]
[(0,'d',[1,2]),(1,'u',[2]),(2,'u',[2,3]),(2,'p',[3]),(3,'p',[3])]  "dup" -> [3]
[(0,'a',[0,2]),(0,'b',[3]),(1,'a',[1]),(1,'b',[1]),(2,'b',[1,4]),(4,'b',[2])]  "aab" -> [3,1,4]
[(0,'a',[0,2]),(0,'b',[3]),(1,'a',[1]),(1,'b',[1]),(2,'b',[1,4]),(4,'b',[2])]  "abb" -> [1,2]


3
这带回了我自动机课程中的恐怖回忆。
Don Thousand

我们可以采取个别线路输入每一个新的状态,例如工作过的例子吗?
ovs

@ovs:当然可以!
ბიმო

Answers:


7

Haskell,66个字节

import Data.List
f d=foldl(\s c->nub[r|(y,r)<-d,g<-s,(g,c)==y])[0]

在线尝试!


nub如果假设状态为[Int],则可以取消导入,然后可以使用check个[0..]有限状态:60个字节
'18

@BWO遍历所有Ints 所有当前状态,因此仍然会产生重复状态。例如(改[0..][0..3]用于测试目的,但这不应该有所作为,对吧?)
OVS

是啊,不知道我在想什么..算了..
ბიმო

4

Brachylog,42个字节

,0{hẸ&t|∋₁B∋IhJ&tJ&hhC∧I∋₁C∧It∋S&hb;B,S↰}ᵘ

输入为[string,nfa],其中nfa是状态转换的列表[初始状态,字母,新状态为列表]

说明

,0                                              # Append 0 to the input (initial state)
  {                                      }ᵘ     # Find all unique outputs
   h                                            # if first element (string)
    Ẹ                                           #   is empty
     &t                                         #   then: return last element (current state)
       |                                        #   else:
        ∋₁B                                     #       save the state transitions in "B"
           ∋I                                   #       take one of these transitions, save in "I"
             hJ                                 #       take the initial state requirement, store in "J"
               &tJ                              #       make sure "J" is actually the current state
                  &hhC                          #       Save first char of string in C
                      ∧I∋₁C                     #       make sure the char requirement for the state transition is the current char
                           ∧It∋S                #       Make "S" equal to one of the new states
                                &hb             #       Behead the string (remove first char)
                                   ;B,S         #       Add B (the state transitions) and S (the new state)
                                       ↰        #       recur this function

在线尝试!


4

Brachylog v2,31个字节

{b,Ȯ,Ȯ\c↔,0↔ġ₃kH&hg;Hz{∋ᵈ}ᵐtt}ᵘ

在线尝试!或更复杂的示例

Brachylog在这类问题上确实很擅长,而在需要两个单独的输入和一个输出的问题上真的很差。该程序几乎全部都在执行。

输入格式是一个包含两个元素的列表:第一个是状态转换([oldState, symbol, newState])列表,第二个是符号列表。我最初计划该程序使用符号的字符代码(因为Brachylog的字符串处理有时可能有点奇怪),但事实证明字符也可以工作(尽管您必须将输入字符串写为字符列表,而不是一个字符串)。如果状态符号对可以转换为多个不同的状态,则可以编写多个转换来处理。

说明

{b,Ȯ,Ȯ\c↔,0↔ġ₃kH&hg;Hz{∋ᵈ}ᵐtt}ᵘ
{                            }ᵘ   Find all distinct outputs that can result from:
 b                                  taking the input minus its first element,
  ,Ȯ                                appending a singleton list (i.e. an element)
    ,Ȯ                              then appending that same element again
      \                             and transposing;
       c                            then concatenating the resulting lists,
        ↔,0↔                        prepending a 0,
            ġ₃                      grouping into blocks of 3 elements
              k                       (and discarding the last, incomplete, block),
               H&                   storing that while we
                 h                  take the first input element,
                  g  z              pair a copy of it with each element of
                   ;H                 the stored value,
                      {  }ᵐ         assert that for each resulting element
                       ∋ᵈ             its first element contains the second,
                        ᵈ ᵐ           returning the list of second elements,
                            t       then taking the last element of
                           t          the last element.

通过查看程序的某些部分版本会产生什么样的结果可能更容易。每次使用以下输入:

[[[0,97,1],[1,97,0],[1,98,1],[1,98,2]],[97,98,97,97,98]]

我们可以观察到该程序的一些前缀的输出:

[[[0,97,1],[1,97,0],[1,98,1],[1,98,2]],[97,98,97,97,98]]b,Ȯ,Ȯ
[[97,98,97,97,98],L,L]

[[[0,97,1],[1,97,0],[1,98,1],[1,98,2]],[97,98,97,97,98]]b,Ȯ,Ȯ\
[[97,A,A],[98,B,B],[97,C,C],[97,D,D],[98,E,E]]

[[[0,97,1],[1,97,0],[1,98,1],[1,98,2]],[97,98,97,97,98]]b,Ȯ,Ȯ\c↔,0↔
[0,97,A,A,98,B,B,97,C,C,97,D,D,98,E,E]

[[[0,97,1],[1,97,0],[1,98,1],[1,98,2]],[97,98,97,97,98]]b,Ȯ,Ȯ\c↔,0↔ġ₃k
[[0,97,A],[A,98,B],[B,97,C],[C,97,D],[D,98,E]]

[[[0,97,1],[1,97,0],[1,98,1],[1,98,2]],[97,98,97,97,98]]b,Ȯ,Ȯ\c↔,0↔ġ₃kH&hg;Hz
[[[[0,97,1],[1,97,0],[1,98,1],[1,98,2]],[0,97,A]],
 [[[0,97,1],[1,97,0],[1,98,1],[1,98,2]],[A,98,B]],
 [[[0,97,1],[1,97,0],[1,98,1],[1,98,2]],[B,97,C]],
 [[[0,97,1],[1,97,0],[1,98,1],[1,98,2]],[C,97,D]],
 [[[0,97,1],[1,97,0],[1,98,1],[1,98,2]],[D,98,E]]]

[[[0,97,1],[1,97,0],[1,98,1],[1,98,2]],[97,98,97,97,98]]b,Ȯ,Ȯ\c↔,0↔ġ₃kH&hg;Hz{∋ᵈ}ᵐ
e.g. [[0,97,1],[1,98,1],[1,97,0],[0,97,1],[1,98,1]]

对于此处的第一个示例,L最初是一个未知元素,但是当我们通过进行转置时\,Brachylog意识到唯一的可能性是与输入长度相同的列表。这里的最后一个例子是不确定的。我们正在使用Brachylog本身的不确定性对NFA中的不确定性进行建模。

可能的改进

这里的某些语法(例如↔,0↔尤其是with的混乱H&hg;Hz{…ᵈ}ᵐ)相当笨拙。如果有一种简短的方式来说明这一点,这也不会令我感到惊讶。

{∋ᵈ}ᵐ它本身就是一个相当可疑的结构-您希望能够编写∋ᵈᵐ-但由于某种原因它无法解析。


∋ᵈᵐ之所以不会进行解析,是因为它的实现方式使得理论上可以使用多字符元谓词名称(如果我们没有单符号可能性)。实际上,当前未使用它。
致命

3

Python 3,103 80字节

感谢@BWO

w=lambda n,f,a={0}:w(n,f[1:],{y for(x,c,y)in n if c==f[0]and{x}&a})if''<f else a

蒂奥

先前的“优雅”列表理解(103字节):

def w(a,b):
    q=[0]
    for c in b:q=[j for s in q for i in a if s in i if i[1]==c for j in i[2]]
    return q

Python 3缺乏的遗憾reduce..但是使用递归和实际设置仍然会使您减少到80个字节
4'21

@BWO好的,谢谢,哈哈顺便说一句,上面是我最喜欢的示例python代码,以显示...一排巨人的列表理解使我比他们应有的更多娱乐
Quintec

我想你可以通过更换节省2个字节if''<fif f
Chas Brown

@Chas Brown如果f是虚假值(例如空字符串)失败
-Quintec

实际上,我的意思是,
不要

3

JavaScript(ES6),99个字节

将输入作为(nfa)(string)。返回一个Set。

a=>g=([c,...b],s=[0])=>c?g(b,a.reduce((p,[x,y,z])=>s.includes(x)&y==c?[...p,...z]:p,[])):new Set(s)

在线尝试!


3

R,81个字节

function(a,b,e,s)Reduce(function(A,x)unique(e[a%in%A&b==x]),el(strsplit(s,"")),0)

在线尝试!

直接使用回答Reduce。注意到规则的三个矢量state, symbol, new-statesa,b,e

规则是分开的(例如,规则0,'a',[1,2]is 0,'a',10,'a',2)。



2

干净,68字节

这个基于ovs的Haskell解决方案的方法比我最初的方法要短一些。

现在包括一个测试装置

import StdEnv
?d=foldl(\s c=removeDup[r\\(y,r)<-d,g<-s|(g,c)==y])[0]

在线尝试!


1
@BWO添加了测试工具
很有可能

1

木炭,44字节

⊞υ⁰Fη«≔υζ≔⟦⟧υFζFθ¿∧⁼§λ⁰κ⁼§λ¹ιF§λ²¿¬№υμ⊞υμ»Iυ

在线尝试!链接是详细版本的代码。说明:

⊞υ⁰

0送到预定义的空白列表以将初始状态设置为{0}

Fη«

循环输入。

≔υζ

复制状态。

≔⟦⟧υ

重置状态。

Fζ

循环遍历状态副本。

Fθ

循环NFA条目。

¿∧⁼§λ⁰κ⁼§λ¹ι

如果条目匹配,则...

F§λ²

...循环遍及新州...

¿¬№υμ

....如果它们还不在列表中...

⊞υμ»

...将它们添加到列表中。

Iυ

将状态列表转换为字符串,以便在单独的行上隐式输出。



1

Japt,31个字节

W=[W]c;Ê?ßUÅVVf!øW føUg)mÌc):Wâ

尝试一下!

通过更好地利用Japt从某些输入隐式形成函数的功能,节省了2个字节

说明:

W=[W]c;                            Initialize the state set to [0] on the first run
       Ê?                   :Wâ    If the input is empty return the unique states; else...
             Vf!øW                 Get the transitions valid for one of the current states
                   føUg)           Of those, get the ones valid for the current character
                        mÌc)       Merge the states of the remaining transitions
         ßUÅV                      Repeat with the remaining characters as input

新的“初始化状态”代码可以使用更多细节。W如果输入少于3个,则Japt初始化为0,因此第一次运行[W][0],并c“展平”一个数组。[0]已经尽可能平坦了,所以它没有改变。例如,在后续运行中W具有不同的值[1,2]。在这种情况下[W]变成[[1,2]],单元素数组,其中该元件是一个数组。这次c解开了包装,回到了[1,2]。因此,在第一次运行是W=[0],在随后的运行是W=W

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.