Выборка последовательностей случайных чисел в Хаскелле

Мне нужны маленькие списки гауссовских случайных чисел для моделирования и таким образом, я попробовал следующее:

import System.Random

seed = 10101
gen = mkStdGen seed

boxMuller mu sigma (r1,r2) =  mu + sigma * sqrt (-2 * log r1) * cos (2 * pi * r2) 

Это - просто алгоритм Коробки-Muller - данный r1, r2 однородные случайные числа в [0,1] интервал, он возвращает гауссовское случайное число.

normals 0 g = [] 
normals n g = take n $ map (boxMuller 0 1) $ pairs $ randoms g
    where pairs (x:y:zs) = (x,y):(pairs zs)

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

Проблема с этим должна быть очевидной: это всегда производит ту же причину последовательности, я использую всегда то же семя! Я не получаю новые последовательности, я только получаю первые n ценности последовательности все время.

Что я симулировал, ясно было это, когда я печатаю:

x = normal 10 
y = normal 50

У меня был бы x, чтобы быть первыми 10 ценностями map (boxMuller 0 1) $ pairs $ randoms g и y, чтобы быть следующими 50 ценностями в этом списке, и так далее.

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

18
задан Rafael S. Calsaverini 21 January 2010 в 15:44
поделиться

3 ответа

Я думаю, что делают ваши расчеты, которые требуют случайных чисел внутри монада, что рефераты прочь генератор является самым чистым. Вот что будет выглядеть этот код:

мы собираемся поставить экземпляр stdgen в государственном монаде, а затем предоставить какой-то сахар по поводу протокола и установленного метода монады штата, чтобы дать нам случайные числа.

Сначала загрузите модули:

import Control.Monad.State (State, evalState, get, put)
import System.Random (StdGen, mkStdGen, random)
import Control.Applicative ((<$>))

(обычно я бы, вероятно, не укажу импорт, но это позволяет легко понять, откуда приходит каждая функция.)

, мы определим наши «вычисления, требующие случайных чисел «Монад; В этом случае псевдоним для состояния stdgen R R . (Потому что «случайно» и «Рэнд» уже означают что-то еще.)

type R a = State StdGen a

Путь R работает: вы определяете вычисление, которое требует потока случайных чисел (монадическое «действие»), а затем вы «запустить его» С Runrandom :

runRandom :: R a -> Int -> a
runRandom action seed = evalState action $ mkStdGen seed

Это принимает действие и семена и возвращает результаты действия. Так же, как обычный оценка , runreader и т. Д. и т. Д.

Теперь нам просто нужен сахар вокруг Государственного монада. Мы используем GOT , чтобы получить , чтобы получить stdgen, и мы используем , чтобы установить новое состояние , чтобы установить новое состояние. Это означает, что чтобы получить одно случайное число, мы бы написали:

rand :: R Double
rand = do
  gen <- get
  let (r, gen') = random gen
  put gen'
  return r

Получаем текущее состояние генератора случайного номера, используйте его, чтобы получить новый случайный номер и новый генератор, сохранить случайное число, установить новое состояние генератора и верните случайное число.

Это «действие», которое можно запускать с Runrandom, поэтому давайте попробуем его:

ghci> runRandom rand 42
0.11040701265689151                           
it :: Double     

Это чистая функция, поэтому, если вы запускаете его снова с теми же аргументами, вы получите тот же результат. Примеси остаются в «действии», которые вы переходите к Рундунду.

В любом случае, ваша функция хочет, чтобы пары случайных чисел, поэтому давайте напишем действие для производства пара случайных чисел:

randPair :: R (Double, Double)
randPair = do
  x <- rand
  y <- rand
  return (x,y)

Запустите это с Runrandom, и вы увидите два разных числа в пара, как вы ожидаете. Но обратите внимание, что вам не нужно было поставить «Rand» с аргументом; Это потому, что функции чистые, но «Рэнд» - это действие, которое не должно быть чистым.

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

boxMuller :: Double -> Double -> (Double, Double) -> Double
boxMuller mu sigma (r1,r2) =  mu + sigma * sqrt (-2 * log r1) * cos (2 * pi * r2)

со всеми функциями / действиями / действиями вспомогательного Написать Нормалы , действие 0 аргументов, которые возвращают (лениво-генерируемый) бесконечный список нормально распределенных двойников:

normals :: R [Double]
normals = mapM (\_ -> boxMuller 0 1 <$> randPair) $ repeat ()

Вы также можете написать это менее кратко, если хотите:

oneNormal :: R Double
oneNormal = do
    pair <- randPair
    return $ boxMuller 0 1 pair

normals :: R [Double]
normals = mapM (\_ -> oneNormal) $ repeat ()

повторить () дает монадическую акцию бесконечный поток ничего не вызывать функции (и это то, что делает результат нормалей бесконечно долго). Я изначально написал [1 ..] , но я переписал его, чтобы удалить бессмысленные 1 из текста программы. Мы не работаем на целых числах, мы просто хотим бесконечный список.

В любом случае, вот и все. Чтобы использовать это в реальной программе, вы просто выполняете свою работу, требующую случайных нормалей внутри R Action:

someNormals :: Int -> R [Double]
someNormals x = liftM (take x) normals

myAlgorithm :: R [Bool]
myAlgorithm = do
   xs <- someNormals 10
   ys <- someNormals 10
   let xys = zip xs ys
   return $ uncurry (<) <$> xys

runRandom myAlgorithm 42

применяются обычные методы для программирования монадических действий; Держитесь как можно меньшее из вашей заявки в R , а вещи не будут слишком грязными.

О, и на другой вещи: лень может «утечка» за пределами монадской границы чисто. Это означает, что вы можете написать:

take 10 $ runRandom normals 42

и это будет работать.

28
ответ дан 30 November 2019 в 07:33
поделиться

Список вы получаете из randoms , бесконечно, и когда вы используете конечный префикс, вам нужно не выбрасывать бесконечный хвост. Вы можете пройти случайные числа в качестве дополнительного параметра и вернуть неиспользуемые случайные числа в качестве дополнительного результата, или вы можете припарковать бесконечную последовательность случайных чисел в монаде государства.

Аналогичная проблема возникает для компиляторов и других кодов, которые хотят подачи уникальных символов. Это просто настоящая боль в Haskell, потому что вы находитесь в состоянии потокового состояния (из генератора случайных чисел или генератора символов) по всему коду.

Я сделал рандомизированные алгоритмы как с явными параметрами, так и с монадом, и ни один действительно не удовлетворяет. Если вы Grok Monads, я, вероятно, возникнут небольшую рекомендацию, чтобы использовать государственный монад, содержащий бесконечный список случайных чисел, которые еще не использовались.

6
ответ дан 30 November 2019 в 07:33
поделиться

Вы также можете обойти эту проблему с использованием newstdgen , а затем вы получите разное семя (практически) каждый раз.

1
ответ дан 30 November 2019 в 07:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: