Хаскель и случайные числа - PullRequest
17 голосов
/ 29 апреля 2010

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

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

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

Ответы [ 4 ]

20 голосов
/ 29 апреля 2010

Как избежать 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, становится довольно уродливым из-за постоянной передачи текущего значения туда и обратно. Это также потенциально подвержено ошибкам, так как было бы легко случайно повторно использовать старое значение. Как упоминалось выше, использование одного и того же значения PRNG даст ту же последовательность чисел, что обычно не то, что вы хотите. Обе проблемы являются прекрасным примером того, где имеет смысл использовать State монаду - которая выходит за рамки темы, но вы можете рассмотреть ее дальше.

11 голосов
/ 30 апреля 2010

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

6 голосов
/ 30 апреля 2010

Если вы хотите получить бесконечный список Integer s, у вас возникнут проблемы, так как вы никогда не получите значение StdGen обратно.Здесь вы хотите сделать split сначала StdGen, снова передать одну половину и «использовать» другую половину, чтобы сгенерировать бесконечный список целых чисел.Примерно так:

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

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

3 голосов
/ 29 апреля 2010

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

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

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

...