Подтвердить что ты не робот

Создайте список случайных значений, а также получите новый генератор

Я использую System.Random и Random typeclass в своем приложении для генерации случайных чисел. Однако я хотел бы сгенерировать список случайных поплавков произвольной длины с помощью функции типа randoms :: StdGen -> Int -> ([Float], StdGen)

Без ограничения на получение нового генератора я мог бы легко написать randoms gen n = (take n $ randoms gen) :: [Float]

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

Как я могу генерировать бесконечный (или произвольный) список случайных значений, а также "обновлять" мой случайный генератор.

4b9b3361

Ответ 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.Listswap из 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, если у вас много повторяющихся шаблонов.