3

Мне нужно проверить, допускает ли автомат слово, которое состоит из одинаковых символов. Если хотя бы одно такое есть, то нужно вывести его, если нет - вывести "NO". Для каждого символа из входного алфавита мы запускаем поиск, и если мы пришли в финальный стан, то должны вывести это слово. Если мы дважды попали не в финальный стан, то прекратить поиск для конкретной буквы(попали в цикл). Я написал часть кода, создал тип автомат, но не знаю как реализовать сам алгоритм на haskell.

main = do { 
print(goal m1);
print(goal m2);
print(goal m3);
print(goal m4);
}

w = "abab" type FSM q = ([q], Alphabet, [Transition q], q, [q]) type Alphabet = [Char] type Transition q = (q, Char, q)

m1 :: FSM Int m1 = ([0, 1, 2, 3], ['a', 'b'], [(0, 'a', 1), (1, 'a', 3), (2, 'a', 2), (0, 'b', 2), (2, 'b', 3), (1, 'b', 1)], 0, [3] )

m2 :: FSM Int m2 = ([0, 1, 2, 3], ['a', 'b'], [(0, 'a', 1), (1, 'a', 3), (2, 'a', 2), (0, 'b', 2), (2, 'b', 3), (1, 'b', 1), (3, 'a', 3), (3, 'b', 3)], 0, [3] )

m3 :: FSM Int m3 = ([0, 1, 2, 3], ['a', 'b'], [(0, 'a', 1), (1, 'a', 3), (2, 'a', 2), (0, 'b', 2), (2, 'b', 3), (1, 'b', 1), (3, 'a', 1), (3, 'b', 3) ], 0, [3] )

m4 :: FSM Int m4 = ([0, 1, 2, 3, 4, 5], ['a', 'b'], [(0, 'a', 1), (1, 'a', 2), (2, 'a', 3), (3, 'a', 5), (5, 'a', 4), (1, 'b', 1), (3, 'b', 1), (3, 'b', 3) ], 0, [4] )

states :: FSM q -> [q] states (u, _, _, _, _) = u

alph :: FSM q -> Alphabet alph (_, a, _, _, _) = a

trans :: FSM q -> [Transition q] trans (_, _, t, _, _) = t

start :: FSM q -> q start (_, _, _, s, _) = s

final :: FSM q -> [q] final (_, _, _, _, f) = f

delta :: FSM Int -> Int -> Char -> Int delta m st symbol | length [q1 | (q0, x, q1) <- trans m, q0 == st, x == symbol] > 0 = [q1 | (q0, x, q1) <- trans m, q0 == st, x == symbol] !! 0 | otherwise = -1

goal:: FSM Int -> String goal m = seek m (alph m) where seek m [] = "No" seek m (x:xs) | find_letter m x > 0 = create_word x (find_letter m x) [] | otherwise = seek m xs

find_letter:: FSM Int -> Char -> Int find_letter m s = dfs m s (start m) [start m] 0 where dfs m s state states count | (delta m state s) elem states = 0 | (delta m state s) == -1 = 0 | (delta m state s) elem (final m) = count + 1 | otherwise = dfs m s (delta m state s) (states++[delta m state s]) (count+1)

create_word:: Char -> Int -> [Char] -> [Char] create_word symbol 0 list = list create_word symbol count list = create_word symbol (count-1) (list++[symbol])

user435351
  • 57
  • 4

1 Answers1

3

Задача сводится к поиску пути в ориентированном графе. Например, для вашего случая m1 введите сюда описание изображения

Нужно найти один из путей из начального состояния (четвертое поле типа FSM) – вершины 0 на диаграмме, до одного из заключительных состояний (пятое поле типа FSM), в данном случае оно одно – это вершина 3. При этом использовать только одинаково промаркированные ребра, например, только a или только b.

Т.е. алгоритм будет таким

  • Берем один из символов алфавита, например, a
  • Удаляем все ребра, промаркированные другими символами. введите сюда описание изображения
  • Ищем путь от вершины 0 до вершины 3 (например, поиском в ширину)

В данном случае это будет путь 0→1→3 через два ребра a, значит автомат допускает как минимум одну подходящую строку – aa

Если путь не найден, повторяем действия для других заключительных состояний и оставшихся символов.


Дополнение после правок в вопросе

Если не менять ваш алгоритм, получится примерно так

import Data.Maybe (fromMaybe, listToMaybe)
import Data.Foldable (asum)
...

delta :: Eq a => FSM a -> a -> Char -> Maybe a delta m st symbol = listToMaybe [q1 | (q0, x, q1) <- trans m, q0 == st, x == symbol]

goal :: Eq a => FSM a -> String goal m = fromMaybe "No" $ asum [flip replicate x <$> find_letter m x | x <- alph m]

find_letter :: Eq a => FSM a -> Char -> Maybe Int find_letter m s = dfs (start m) [start m] 1 where dfs state seen count = case delta m state s of Nothing -> Nothing Just nextState | nextState elem seen -> Nothing | nextState elem final m -> Just count | otherwise -> dfs nextState (nextState : seen) (count + 1)

Но имейте в виду, что в изначальном вопросе у вас был один недетерминированный автомат m4, и с ним, по понятной причине алгоритм не сработает.

От себя могу предложить такой вариант: Так как минимальное количество символов в допустимом слове у вас не может превысить количество состояний, можно просто скармливать автомату по одной букве, пока не достигнем заключительного состояния или пока не превысим количество узлов.

Вот пример для недетерминированного автомата.

m4 :: FSM Int
m4 = ([0,1,2,3,4,5,6,7],
      ['a', 'b'],
      [(0,'a',1), (1,'b',1), (1,'a',4), (0,'a',3),
      (0,'b',2), (3,'b',2), (2,'b',5), (5,'a',5),
      (4,'b',6), (5,'a',6), (6,'b',3), (3,'a',5),
      (6,'a',7), (7,'b',7), (7,'a',4)],
      0,
      [7])

next :: Eq a => FSM a -> Char -> a -> [a] next m char st = [to | (from, x, to) <- trans m, x == char, st == from]

GHCi> take (length (states m4)) $ iterate (nub . (>>= next m4 'a')) [start m4]
[[0],[1,3],[4,5],[5,6],[5,6,7],[5,6,7,4],[5,6,7,4],[5,6,7,4]]
GHCi> take (length (states m4)) $ iterate (nub . (>>= next m4 'b')) [start m4]
[[0],[2],[5],[],[],[],[],[]]

Для буквы a заключительное состояние 7 достигается на пятом шаге, значит в слове 4 буквы a. Для буквы b заключительное состояние не достигается.

extrn
  • 10,941
  • Так это понятно, мне код нужен) Вообще, я уже написал его, но он прямо скажем не очень – user435351 Mar 22 '21 at 22:00
  • @user435351 "не очень" - это не страшно, приложите то, что есть к вопросу. Здесь не принято делать домашнюю работу за студентов, а помогать в написании - совсем другое дело. – extrn Mar 22 '21 at 22:14
  • обновил код, посмотрите, если можете - хотелось бы упростить функции – user435351 Mar 22 '21 at 22:38
  • а можно узнать, как работает эта строчка: goal m = fromMaybe "No" $ asum [flip replicate x <$> find_letter m x | x <- alph m] ? – user435351 Mar 23 '21 at 12:45
  • @user435351 в случае Maybe, asum возвращает первый Just (из списка) или Nothing, если таких нет. fromMaybe возвращает первый аргумент, если второй - Nothing, иначе возвращает содержимое второго. – extrn Mar 23 '21 at 15:23
  • @user435351 flip replicate x <$> m, применяет функцию flip replicate x, т.е. \y -> replicate y x к содержимому m, если оно есть. иначе ничего не делает. т.е. flip replicate 'x' <$> Just 5 = Just (replicate 5 'x'), flip replicate 'x' <$> Nothing = Nothing – extrn Mar 23 '21 at 15:35
  • @user435351 Другими словами, берем символы, делаем из них "возможно числа", из них "возможно строки", первую такую "точно строку" берем и возвращаем ее содержимое. Если таких не оказалось - возвращаем "No" – extrn Mar 23 '21 at 15:52
  • розобрался, но есть один момент - здесь мы будем находить все возможные варианты слов из одной буквы, и только потом брать первое слово. Можно ли "найти первое слово и тут же взять"? – user435351 Mar 23 '21 at 16:24
  • @user435351 Не будем. ленивые вычисления же. Вычисление списка остановится на первом Just – extrn Mar 23 '21 at 16:40
  • 1
    тогда это круто) – user435351 Mar 23 '21 at 16:58