Моделюйте NFA


15

Недетермінірованного кінцевий автомат є кінцевим автоматом , де кортеж (стате,сумбол) відображаються в декількох штатів. Тобто ми замінюємо звичайну δ:Q×ΣQ  функцію переходу DFA на іншу функцію Δ:Q×ΣП(Q) .

Якщо ви знаєте, що таке NFA, можливо, ви захочете пропустити наступний розділ.

Формальне визначення

NFA унікально описано

  • кінцевий набір станівQ
  • кінцевий набір символівΣ
  • функція переходуΔ:Q×ΣП(Q)
  • початковий станq0Q
  • сукупність кінцевих станівЖQ

Машина запускається в і зчитує кінцевий рядок символів w Σ , для кожного символу він одночасно застосує функцію функції переходу з поточним станом і додасть кожен новий набір станів до набору поточних станів.q0шΣ

Виклик

Для цього завдання ми будемо ігнорувати , щоб спростити його, крім того, алфавіт буде завжди (рядкові) літери до г і безлічі станів буде { 0 ... N } для деякого цілого невід'ємного числа N . Початковий стан завжди буде 0 .Ж а z {0N}N0

Давши слово та опис NFA, ваше завдання - визначити всі кінцеві стани.ш{аz}

Приклад

Розглянемо рядок та наступний опис:абааб

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

Машина запуститься в :q0=0

  1. читати : нові стани { 1 }а{1}
  2. read a : нові стани { 1 , 2 }б{1,2}
  3. читати : нові стани { 0 }а{0}
  4. читати : нові стани { 1 }а{1}
  5. read a : нові стани { 1 , 2 }б{1,2}

Отже, кінцеві стани і, таким чином, вихід будуть .{1,2}

Примітка: На кроці (2) перехід стану відображається у ∅, оскільки опис включає лише переходи до не порожніх множин.2

Правила

Вхід буде складатися з рядка та якогось опису NFA (без -переходів):ϵ

  • рядок вводу завжди буде елементом {аz}
  • дійсні входи (не обмежуються):
    • список / масив кортежів / списків
    • новий рядковий вхід
  • опис NFA буде містити лише переходи з не порожніми наборами як результат
    • ви можете скоротити правила з тими самими символами, якщо їх результат однаковий (наприклад, правила 0,'a',[1,2]і 0,'b',[1,2]може бути скорочений0,"ab",[1,2]
    • ви можете прийняти кожне правило окремо (наприклад, правило 0,'a',[1,2]може бути 0,'a',[1]і 0,'a',[2])
  • Ви можете вибрати великі літери, якщо хочете
  • ви можете взяти кількість станів як вхідні дані
  • Ви можете припустити якесь упорядкування входів (наприклад, упорядковано за станом чи символами)

Виходом буде список / набір / розділений новий рядок вихідний і т.д. кінцевих станів

  • порядок не має значення
  • без дублікатів (оскільки це набір)

Тестові справи

Ці приклади будуть у форматі, description word -> statesде 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
Це повертає страхітливі спогади з мого курсу автомата.
Дон Тисяча

Чи можемо ми взяти вклад з окремими рядками для кожного нового стану, наприклад, це для працюючого прикладу?
ов

@ovs: Безумовно, іди далі!
ბიმო

Відповіді:


7

Haskell , 66 байт

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

Спробуйте в Інтернеті!


Ви можете позбутися імпорту, nubякщо, якщо вважаєте, що це стан [Int], то ви можете використовувати перевірку, [0..]яка є кінцевою: 60 байт
четвер

@BWO Це повторює всі Ints та над усіма поточними станами, тому все ще створює повторювані стани. Приклад (змінено [0..]на [0..3]тестувальні цілі, але це не повинно змінити значення, правда?)
п.

Так, не впевнений, про що я думав .. Не
забудь

4

Брахілог , 42 байти

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

введіть як [рядок, 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}ᵘ

Спробуйте в Інтернеті! ( або зі складнішим прикладом )

Брахілог дійсно хороший у подібних проблемах, і дуже поганий у проблемах, які потребують двох окремих входів та вихідних даних. Майже вся ця програма - просто сантехніка.

Формат введення - це список, що містить два елементи: перший - це список переходів стану ( [oldState, symbol, newState]), а другий - список символів. Я спочатку планував цю програму працювати з кодами символів для символів (адже обробка рядків Брахілога часом може бути дещо дивною), але виявляється, що символи також працюють (хоча вам потрібно вводити рядок як список символів, а не як рядок). Якщо пара символ-стан може переходити до декількох різних станів, ви пишете кілька переходів для вирішення цього питання.

Пояснення

{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це спочатку невідомий елемент, але коли ми переносимо його через \, Брахілог розуміє, що єдиною можливістю є список такої ж довжини, що і вхідний. Останній приклад тут недетермінований; ми моделюємо недетермінізм у NFA, використовуючи недетермінізм у самому Брахілогі.

Можливі поліпшення

Деякі з синтаксису тут, як, ↔,0↔і особливо безлад 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 байт .
ბიმო

@BWO приємно, спасибі, ха-ха, вище, це мій новий улюблений приклад пітонного коду, щоб показати ... один рядок гігантських списків розуміння розважає мене набагато більше, ніж слід
Quintec

Я думаю, ви можете зберегти 2 байти, замінивши if''<fна if f.
Час Браун

@Chas Браун, який не вдається, якщо f - це помилкове значення, наприклад порожня рядок
Quintec

Насправді, що я говорю, ігноруйте це
Квінтек


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-statesвикликанихa,b,e .

Правила є окремими (наприклад, правило 0,'a',[1,2]є 0,'a',1і 0,'a',2).



2

Чисто , 68 байт

Цей на основі Hassll рішення ovs трохи коротший, ніж був у мого початкового підходу.

тепер включає тестовий джгут

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

Спробуйте в Інтернеті!


1
@BWO Тестовий ремінь додано
Οurous

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â

Спробуй це!

Збережено 2 байти з кращим використанням здатності Japt неявно формувати функцію з деяких входів

Пояснення:

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

Новий код "станів ініціалізації" може використовувати трохи детальніше. Japt ініціалізується Wна 0, якщо є менше 3-х входів, тож при першому запуску [W]є [0]і c"розгладжує" масив. [0]це вже настільки ж плоско, як це стає, тому його не змінюють. На наступних пробігах Wмає інше значення, наприклад [1,2]. У такому разі [W]стає [[1,2]]одноелементним масивом, де цей елемент є масивом. Цього разу це cрозгортається та повертається до [1,2]. Таким чином, на першому запуску це, W=[0]а на наступних - це W=W.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.