Создайте список случайных значений, а также получите новый генератор
Я использую System.Random
и Random
typeclass в своем приложении для генерации случайных чисел. Однако я хотел бы сгенерировать список случайных поплавков произвольной длины с помощью функции типа randoms :: StdGen -> Int -> ([Float], StdGen)
Без ограничения на получение нового генератора я мог бы легко написать
randoms gen n = (take n $ randoms gen) :: [Float]
Однако это оставляет меня с тем же случайным генератором, с которого я начал, что означает, что если бы я дважды запускал эту функцию в строке, я бы получил тот же список, если бы не пошел и не использовал генератор в другом месте, чтобы получить новый.
Как я могу генерировать бесконечный (или произвольный) список случайных значений, а также "обновлять" мой случайный генератор.
Ответы
Ответ 1
Хорошо, посмотрим на функцию, которая у вас есть:
random :: StdGen -> (Float, StdGen) -- From System.Random
Мы можем обернуть это в монаду State
, чтобы получить вычисление с учетом состояния:
state :: (s -> (a, s)) -> State s a -- From Control.Monad.Trans.State
random' :: State StdGen Float
random' = state random
Теперь мы можем сгенерировать кучу поплавков, используя replicateM
:
replicateM :: (Monad m) => Int -> m a -> m [a] -- From Control.Monad
randoms' :: Int -> State StdGen [Float]
randoms' n = replicateM n random'
Наконец, мы разворачиваем State
, чтобы вернуть явную передачу генератора:
randoms :: Int -> StdGen -> ([Float], StdGen)
randoms n = runState (randoms' n)
Если вы объедините все это в одно определение функции, вы получите:
randoms :: Int -> StdGen -> ([Float], StdGen)
randoms n = runState (replicateM n (state random))
Другими словами, мы можем описать процесс как:
- wrap
random
в монаде State
- повторить его
n
раз
- развернуть его
Вот почему монады - такая важная концепция. Вещи, которые могут показаться сложными сначала, - это простые вычисления при просмотре через объектив интерфейса монады.
Ответ 2
Ответ Габриэля верен, и это почти так, как реализуется пакет MonadRandom (состояние Monad, параметризованное случайным генератором).
Это позволяет вам определять его каждый раз, а также поставляется с трансформатором Monad, поэтому вы можете преобразовать любую другую Monad в одну, которая также может создавать случайные значения.
Ваш пример может быть легко реализован как:
(runRand $ take n `fmap` getRandoms) :: RandomGen g => g -> ([Int], g)
StdGen является экземпляром RandomGen, поэтому вы можете просто подключить его и перейти!
Ответ 3
Альтернатива без State
или split
, используя mapAccumL
из Data.List
(и swap
из Data.Tuple
):
nRandoms n gen = mapAccumL(\g _ -> swap $ random g) gen [1..n]
хотя я должен сказать, что у меня нет убедительного аргумента в пользу того, почему это должно быть лучше в любом случае.
Ответ 4
Вы можете определить функцию, тип которой соответствует той, которую вы говорите хотите, хотя и в целом.
import System.Random
randoms' :: (RandomGen g, Random a) => g -> Int -> ([a], g)
randoms' g n =
let (g1, g2) = split g
in (take n $ randoms g1, g2)
Даже если он использует split
split :: g -> (g, g)
Операция split
позволяет получить два разных генератора случайных чисел. Это очень полезно в функциональных программах (например, при передаче генератора случайных чисел до рекурсивных вызовов), но очень мало работы было сделано для статистически надежных реализаций split
& hellip;
он по-прежнему не делает то, что вы хотите. (Я использую Bool
в приведенных ниже примерах для более простого визуального сравнения.)
ghci> g <- getStdGen
ghci> randoms' g 5 :: ([Bool], StdGen)
([False,False,False,True,False],1648254783 2147483398)
ghci> randoms' g 5 :: ([Bool], StdGen)
([False,False,False,True,False],1648254783 2147483398)
Обратите внимание, что случайные массивы одинаковы.
Несмотря на то, что функция работает с проблемой расщепления генератора, мы быстро ее отбрасываем. Вместо этого используйте g2
, поместив его в следующий вызов, как в
ghci> let (a1,g2) = randoms' g 5 :: ([Bool], StdGen)
ghci> let (a2,_) = randoms' g2 5 :: ([Bool], StdGen)
ghci> (a1,a2)
([False,False,False,True,False],[True,True,True,False,True]
Если ваш код работает в монаде IO
, вы можете использовать setStdGen
для замены генератора глобальных случайных чисел в конце, как в
myAction :: Int -> IO ([Float],[Float])
myAction n = do
g <- getStdGen
let (f1,g2) = randoms' g n
let (f2,g3) = randoms' g2 n
setStdGen g3
return (f1, f2)
Состояние потоков вокруг неловко и подвержено ошибкам. Рассмотрите возможность использования State
или ST
, если у вас много повторяющихся шаблонов.