从Haskell中的马尔可夫链生成序列

时间:2014-08-13 12:55:20

标签: haskell

我想从马尔可夫链中生成随机序列。要生成马尔可夫链,我使用以下代码。

module Main where

import qualified Control.Monad.Random as R
import qualified Data.List as L
import qualified Data.Map as M

type TransitionMap = M.Map (String, String) Int
type MarkovChain = M.Map String [(String, Int)]

addTransition :: (String, String) -> TransitionMap -> TransitionMap
addTransition k = M.insertWith (+) k 1

fromTransitionMap :: TransitionMap -> MarkovChain
fromTransitionMap m =
  M.fromList [(k, frequencies k) | k <- ks]
  where ks = L.nub $ map fst $ M.keys m
        frequencies a = map reduce $ filter (outboundFor a) $ M.toList m
        outboundFor a k = fst (fst k) == a
        reduce e = (snd (fst e), snd e)

收集统计数据并生成马尔可夫链对象后,我想生成随机序列。我可以想象这个方法看起来像那样(伪代码)

generateSequence mc s
  | s == "." = s
  | otherwise = s ++ " " ++ generateSequence mc s'
  where s' = drawRandomlyFrom $ R.fromList $ mc ! s

如果有人能向我解释我应该如何实现这个功能,我将不胜感激。

修改

如果有人感兴趣,那就不像我想象的那么困难。

module Main where

import qualified Control.Monad.Random as R
import qualified Data.List as L
import qualified Data.Map as M

type TransitionMap = M.Map (String, String) Rational
type MarkovChain = M.Map String [(String, Rational)]

addTransition :: TransitionMap -> (String, String) -> TransitionMap
addTransition m k = M.insertWith (+) k 1 m

fromTransitionMap :: TransitionMap -> MarkovChain
fromTransitionMap m =
  M.fromList [(k, frequencies k) | k <- ks]
  where ks = L.nub $ map fst $ M.keys m
        frequencies a = map reduce $ filter (outboundFor a) $ M.toList m
        outboundFor a k = fst (fst k) == a
        reduce e = (snd (fst e), snd e)

generateSequence :: (R.MonadRandom m) => MarkovChain -> String -> m String
generateSequence m s
  | not (null s) && last s == '.' = return s
  | otherwise = do
                s' <- R.fromList $ m M.! s
                ss <- generateSequence m s'
                return $ if null s then ss else s ++ " " ++ ss

fromSample :: [String] -> MarkovChain
fromSample ss = fromTransitionMap $ foldl addTransition M.empty $ concatMap pairs ss
  where pairs s = let ws = words s in zipWith (,) ("":ws) ws

sample :: [String]
sample = [ "I am a monster."
         , "I am a rock star."
         , "I want to go to Hawaii."
         , "I want to eat a hamburger."
         , "I have a really big headache."
         , "Haskell  is a fun language."
         , "Go eat a big hamburger."
         , "Markov chains are fun to use."
         ]

main = do
  s <- generateSequence (fromSample sample) ""
  print s

唯一的小烦恼是假的""起始节点。

2 个答案:

答案 0 :(得分:1)

不确定这是否是您要找的。这可以编译:

generateSequence :: (R.MonadRandom m) => MarkovChain -> String -> m String
generateSequence mc s  | s == "." = return s
                       | otherwise = do  
                            s' <- R.fromList $ rationalize (mc M.! s)
                            s'' <- generateSequence mc s'
                            return $ s ++ " " ++ s'' 

rationalize :: [(String,Int)] -> [(String,Rational)]
rationalize = map  (\(x,i) -> (x, toRational i))

答案 1 :(得分:1)

所有随机数生成都需要在Random monad或IO monad中进行。出于您的目的,最简单的方法是使用IO了解如何在evalRandIO monad中执行此操作。在下面的示例中,getRandom是我们要使用的函数。现在getRandomRandom monad中运行,但我们可以使用evalRandIO将其提升到IO monad,如下所示:

main :: IO ()
main = do
  x <- evalRandIO getRandom :: IO Double
  putStrLn $ "Your random number is " ++ show x

注意:我们必须将类型签名添加到绑定x的行的原因是因为在此特定示例中没有其他提示告诉编译器我们想要x的类型。但是,如果我们以某种方式使用x表明我们希望它是Double(例如,乘以另一个Double),则类型签名将不会是必要的。

使用您的MarkovChain类型,对于当前状态,您可以轻松获得[( nextState , 概率 {/> EM> )]。 (我松散地使用“概率”这个词,它不需要是一个真实的概率;任何数字权重都很好)。这是fromListControl.Monad.Random的设计目标。同样,它在Random monad中运行,但我们可以使用evalRandIO将其提升到IO monad。假设transitions是您的转化列表,其类型为[( nextState , 概率 )]。然后,在IO monad中,您可以调用:

nextState <- evalRandIO $ fromList transitions

您可能希望创建一个在Random monad中运行的自己的函数,如下所示:

getRandomTransition :: RandomGen g => MarkovChain -> String -> Rand g String
getRandomTransition currState chain = do
    let transitions = lookup currState chain
    fromList transitions

然后,您可以使用IOevalRandIO monad中调用此函数,例如

nextState <- evalRandIO $ getRandomTransition chain