2010-04-29 4 views
16

Я несколько дней возился с Haskell и наткнулся на проблему.Haskell и случайные числа

Мне нужен метод, который возвращает случайный список целых чисел (Rand [[Int]]).

Итак, я определил тип: type Rand a = StdGen -> (a, StdGen). Я смог произвести Rand IO Integer и Rand [IO Integer] ((returnR lst) :: StdGen -> ([IO Integer], StdGen)) как-то. Любые советы, как производить Rand [[Int]]?

ответ

20

Как избежать IO зависит от того, почему он вводится в первую очередь. Хотя генераторы псевдослучайных чисел по своей природе ориентированы на состояние, нет причин, по которым необходимо задействовать IO.

Я собираюсь угадать и сказать, что вы используете newStdGen или getStdGen, чтобы получить ваш первоначальный PRNG. Если это так, то нет возможности полностью избежать IO. Вы могли бы вместо этого перенести PRNG на mkStdGen, имея в виду, что одно и то же семя приведет к той же последовательности случайных чисел.

Скорее всего, что вы хотите сделать, это получить PRNG внутри IO, а затем передать это как аргумент чистой функции. Разумеется, все будет по-прежнему завернуто в IO, но промежуточные вычисления ему не понадобятся. Вот краткий пример, чтобы дать вам идею:

import System.Random 

type Rand a = StdGen -> (a, StdGen) 

getPRNG = do 
    rng <- newStdGen 
    let x = usePRNG rng 
    print x 

usePRNG :: StdGen -> [[Int]] 
usePRNG rng = let (x, rng') = randomInts 5 rng 
        (y, _) = randomInts 10 rng' 
       in [x, y] 

randomInts :: Int -> Rand [Int] 
randomInts 0 rng = ([], rng) 
randomInts n rng = let (x, rng') = next rng 
         (xs, rng'') = randomInts (n - 1) rng' 
        in (x:xs, rng'') 

Вы можете заметить, что код, использующий ПГСЧ получает довольно некрасиво из-за прохождение текущего значения назад и вперед постоянно. Это также потенциально подвержено ошибкам, так как было бы легко случайно повторно использовать старое значение. Как упоминалось выше, использование того же значения PRNG даст ту же последовательность чисел, которая обычно не та, что вы хотите. Обе проблемы являются прекрасным примером того, где имеет смысл использовать монаду State, которая здесь выходит из темы, но вы можете захотеть взглянуть на нее дальше.

3

Использование монадическое обозначения, вы должны быть в состоянии написать что-то вроде

randomList gen = do 
    randomLength <- yourRandomInteger 
    loop gen (randomLength + 1) 
where 
    loop gen 1 = gen 
    loop gen n = do { x <- gen; xs <- loop gen (n - 1); return (x:xs) } 

И с этим

randomInts :: Rand [Int] 
randomInts = randomList yourRandomInteger 

randomLists :: Rand [[Int]] 
randomLists = randomList randomInts 

Относительно сам монадических вычислений, взгляните на this article. Обратите внимание, что случайные генераторы чисты, вам не нужно ничего IO для этой цели.

+1

момент, когда вы просите образец, вы попадаете в монаду IO –

6

Если вы хотите, чтобы получить бесконечный список Integer с вы собираетесь столкнуться с проблемами, как вы никогда не получите значение StdGen обратно. То, что вы хотите сделать здесь, - splitStdGen, сначала пропустите половину и снова используйте вторую половину, чтобы создать бесконечный список целых чисел. Что-то вроде этого:

infiniteRandomInts :: Rand [Int] 
infiniteRandomInts g = (ints, g2) where 
    (g1,g2) = split g 
    ints = randoms g1 

Однако, если вы повторите этот подход, чтобы получить бесконечную матрицу Integer с (которую вы, кажется, хотите, с помощью Rand [[Int]]), вы можете столкнуться с проблемами статистического характера: Я не знаю, насколько хорошо StdGen занимается повторением split ting. Возможно, другая реализация RandomGen может быть лучше, или вы можете попытаться использовать какую-то диагональную прогулку, чтобы превратить [Int] в [[Int]].

11

Вы воссоздаете MonadRandom на Hackage. Если это больше, чем просто эксперимент, чтобы увидеть, можете ли вы это сделать, вы можете использовать эту библиотеку вместо этого.