Mô phỏng một NFA


15

Máy tự động hữu hạn không điều kiện là một máy trạng thái hữu hạn trong đó một tuple được ánh xạ tới nhiều trạng thái. I E. chúng tôi thay thế hàm chuyển đổi của DFA bằng một hàm khác .δ : Q × Σ Q(Stmộtte,Symbotôi)δ:Q×ΣQ Δ:Q×ΣP(Q)

Nếu bạn biết NFA là gì, bạn có thể muốn bỏ qua phần tiếp theo.

Định nghĩa chính thức

Một NFA được mô tả duy nhất bởi

  • Q một tập hợp hữu hạn các trạng thái
  • Σ một tập hợp các ký hiệu hữu hạn
  • Δ:Q×ΣP(Q) chức năng chuyển đổi
  • q0Q trạng thái ban đầu
  • FQ một tập hợp các trạng thái cuối cùng

Máy khởi động trong và đọc một chuỗi ký hiệu hữu hạn , với mỗi ký hiệu, nó sẽ đồng thời áp dụng chức năng chuyển đổi với trạng thái hiện tại và thêm từng bộ trạng thái mới vào bộ trạng thái hiện tại. w Σ *q0wΣ*

Thử thách

Đối với thử thách này, chúng tôi sẽ bỏ qua để đơn giản hóa nó, hơn nữa bảng chữ cái sẽ luôn là các chữ cái (chữ thường) to và tập hợp các trạng thái sẽ là cho một số nguyên không âm . Trạng thái ban đầu sẽ luôn là .một z { 0 ... N } N 0F a z {0N}N0

Đưa ra một từ và mô tả về NFA, nhiệm vụ của bạn là xác định tất cả các trạng thái cuối cùng.w{az}

Thí dụ

Hãy xem xét chuỗi abaab và mô tả sau:

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

Máy sẽ khởi động sau q0=0 :

  1. đọc một a : trạng thái mới {1}
  2. đọc một b : trạng thái mới {1,2}
  3. đọc một a : trạng thái mới {0}
  4. đọc một a : trạng thái mới {1}
  5. đọc một b : trạng thái mới {1,2}

Vì vậy, các trạng thái cuối cùng và do đó đầu ra sẽ là {1,2} .

Lưu ý: Trong bước (2) việc chuyển trạng thái ánh xạ sang vì mô tả chỉ bao gồm các chuyển đổi sang các tập hợp không trống.2

Quy tắc

Đầu vào sẽ bao gồm một chuỗi và một số loại mô tả về NFA (không có -transitions):ϵ

  • chuỗi đầu vào sẽ luôn là phần tử của{az}
  • đầu vào hợp lệ (không giới hạn):
    • danh sách / mảng của bộ / danh sách
    • đầu vào tách dòng mới
  • mô tả của NFA sẽ chỉ chứa các hiệu ứng chuyển tiếp với các tập hợp không trống.
    • bạn có thể viết tắt các quy tắc với các nhân vật tương tự nếu kết quả của họ là như nhau (ví dụ. các quy định 0,'a',[1,2]0,'b',[1,2]có thể được viết tắt với0,"ab",[1,2]
    • bạn có thể tách riêng từng quy tắc (ví dụ: quy tắc 0,'a',[1,2]có thể 0,'a',[1]0,'a',[2])
  • bạn có thể chọn chữ in hoa nếu bạn muốn
  • bạn có thể lấy số lượng trạng thái làm đầu vào
  • bạn có thể giả định một số loại thứ tự của đầu vào (ví dụ: được sắp xếp theo trạng thái hoặc ký hiệu)

Đầu ra sẽ là đầu ra được phân tách danh sách / bộ / dòng mới, vv của các trạng thái cuối cùng

  • thứ tự không quan trọng
  • không trùng lặp (vì nó là một bộ)

Các trường hợp thử nghiệm

Các ví dụ này sẽ ở định dạng description word -> statestrong đó descriptionmột danh sách các bộ dữ liệu (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
Điều này mang lại những kỷ niệm đáng sợ từ khóa học tự động của tôi.
Don Ngàn

Chúng ta có thể lấy đầu vào với các dòng riêng lẻ cho từng trạng thái mới, ví dụ: đây là ví dụ hoạt động không?
trứng

@ovs: Chắc chắn đi trước!
ბიმო

Câu trả lời:


7

Haskell , 66 byte

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

Hãy thử trực tuyến!


Bạn có thể thoát khỏi việc nhập khẩu cho nubnếu bạn cho rằng các quốc gia được [Int], sau đó bạn có thể sử dụng kiểm tra từng [0..]đó là hữu hạn: 60 byte
ბიმო

@BWO Điều này lặp đi lặp lại trên tất cả các Ints trên tất cả các trạng thái hiện tại và do đó vẫn tạo ra các trạng thái trùng lặp. Ví dụ (được đổi [0..]thành [0..3]mục đích thử nghiệm, nhưng điều này sẽ không tạo ra sự khác biệt, phải không?)
ovs

Yeah, không chắc chắn những gì tôi đã suy nghĩ .. Nevermind ..
ბიმო

4

Brachylog , 42 byte

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

đầu vào là [chuỗi, nfa] trong đó nfa là danh sách chuyển trạng thái [trạng thái ban đầu, chữ cái, trạng thái mới dưới dạng danh sách]

Giải trình

,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

Hãy thử trực tuyến!


4

Brachylog v2, 31 byte

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

Hãy thử trực tuyến! ( hoặc với một ví dụ phức tạp hơn )

Brachylog thực sự giỏi trong loại vấn đề này, và thực sự tệ ở những vấn đề đòi hỏi hai đầu vào và đầu ra riêng biệt. Hầu như tất cả các chương trình này chỉ là hệ thống ống nước.

Định dạng đầu vào là một danh sách chứa hai thành phần: đầu tiên là danh sách các trạng thái chuyển tiếp ( [oldState, symbol, newState]) và thứ hai là danh sách các ký hiệu. Ban đầu tôi dự định chương trình này hoạt động với mã ký tự cho các ký hiệu (vì đôi khi việc xử lý chuỗi của Brachylog có thể hơi kỳ lạ), nhưng hóa ra các ký tự cũng hoạt động (mặc dù bạn phải viết chuỗi đầu vào dưới dạng danh sách các ký tự, chứ không phải như một chuỗi). Nếu một cặp biểu tượng trạng thái có thể chuyển sang nhiều trạng thái khác nhau, bạn viết nhiều chuyển tiếp để đối phó với điều đó.

Giải trình

{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.

Có lẽ dễ dàng hơn để làm theo điều này bằng cách xem những gì một số phiên bản một phần của chương trình sẽ tạo ra. Sử dụng đầu vào sau mỗi lần:

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

chúng ta có thể quan sát đầu ra của một số tiền tố của chương trình này:

[[[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]]

Đối với ví dụ đầu tiên ở đây, Lban đầu là một yếu tố không xác định, nhưng khi chúng tôi chuyển nó qua \, Brachylog nhận ra rằng khả năng duy nhất là một danh sách có cùng độ dài với đầu vào. Ví dụ cuối cùng ở đây là không điều kiện; chúng tôi đang mô hình hóa thuyết không điều kiện trong NFA bằng cách sử dụng thuyết không điều kiện trong chính Brachylog.

Cải tiến có thể

Một số cú pháp ở đây, như ↔,0↔và đặc biệt là sự lộn xộn H&hg;Hz{…ᵈ}ᵐ, khá là rắc rối . Nó sẽ không làm tôi ngạc nhiên nếu có một cách khó khăn hơn để phát hiện ra điều này.

{∋ᵈ}ᵐtheo đúng nghĩa của nó là một cấu trúc khá đáng ngờ - bạn mong đợi chỉ có thể viết ∋ᵈᵐ- nhưng nó không phân tích cú pháp vì một số lý do.


∋ᵈᵐkhông phân tích cú pháp bởi vì nó được triển khai sao cho các tên vị ngữ meta nhiều ký tự trong lý thuyết có thể được sử dụng (nếu chúng ta hết khả năng ký hiệu đơn). Trong thực tế, nó hiện không được sử dụng.
Gây tử vong vào

3

Python 3, 103 80 byte

cảm ơn @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

TIO

Hiểu danh sách "thanh lịch" trước đó (103 byte):

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

Thật xấu hổ khi Python 3 thiếu reduce.. Nhưng sử dụng đệ quy và các bộ thực tế vẫn đưa bạn xuống tới 80 byte .
ბიმო

@BWO tốt đẹp, cảm ơn, haha ​​btw ở trên là mã python ví dụ yêu thích mới của tôi để hiển thị ... một dòng danh sách khổng lồ hiểu được cách của tôi nhiều hơn họ nên
Quintec

Tôi nghĩ bạn có thể tiết kiệm 2 byte bằng cách thay thế if''<fbằng if f.
Chas Brown

@Chas Brown không thành công nếu f là giá trị giả, chẳng hạn như chuỗi rỗng
Quintec

Trên thực tế, tôi đang nói gì, bỏ qua điều đó
Quintec

3

JavaScript (ES6), 99 byte

Đưa đầu vào là (nfa)(string). Trả về một bộ.

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)

Hãy thử trực tuyến!


3

R , 81 byte

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

Hãy thử trực tuyến!

Trả lời đơn giản bằng cách sử dụng Reduce. Có quy tắc như ba vectơ state, symbol, new-statesđược gọi a,b,e.

Các quy tắc là riêng biệt (ví dụ: quy tắc 0,'a',[1,2]0,'a',10,'a',2).



2

Sạch , 68 byte

Cái này dựa trên giải pháp Haskell của ovs ngắn hơn một chút so với cách tiếp cận ban đầu của tôi.

bây giờ bao gồm một khai thác thử nghiệm

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

Hãy thử trực tuyến!


1
@BWO Đã thêm khai thác thử nghiệm
urous

1

Than , 44 byte

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

Hãy thử trực tuyến! Liên kết là phiên bản dài dòng của mã. Giải trình:

⊞υ⁰

Nhấn 0vào danh sách trống được xác định trước để đặt trạng thái ban đầu thành{0}.

Fη«

Lặp lại đầu vào.

≔υζ

Sao chép trạng thái.

≔⟦⟧υ

Đặt lại trạng thái.

Fζ

Vòng qua bản sao của nhà nước.

Fθ

Lặp lại các mục NFA.

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

Nếu mục nhập khớp, thì ...

F§λ²

... vòng qua các trạng thái mới ...

¿¬№υμ

.... nếu họ chưa có trong danh sách ...

⊞υμ»

... Thêm chúng vào danh sách.

Iυ

Truyền danh sách các trạng thái thành chuỗi cho đầu ra ngầm định trên các dòng riêng biệt.



1

Japt , 31 byte

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

Thử nó!

Đã lưu 2 byte với khả năng sử dụng tốt hơn khả năng của Japt để tạo thành một hàm trong một số đầu vào

Giải trình:

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

Mã "trạng thái khởi tạo" mới có thể sử dụng chi tiết hơn một chút. Japt khởi Wlà 0 nếu có ít hơn 3 đầu vào, vì vậy trên đường chạy trốn đầu tiên [W][0], và c"flattens" một mảng. [0]đã bằng phẳng như nó được, vì vậy nó không thay đổi. Trong các lần chạy tiếp theo Wcó một giá trị khác nhau, ví dụ [1,2]. Trong trường hợp đó [W]trở thành [[1,2]], một mảng phần tử đơn trong đó phần tử đó là một mảng. Lần này cmở khóa và trở lại [1,2]. Do đó, trong lần chạy đầu tiên, nó W=[0]và các lần chạy tiếp theo W=W.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.