Мне нужны маленькие списки гауссовских случайных чисел для моделирования и таким образом, я попробовал следующее:
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 ценностями в этом списке, и так далее.
Конечно, это невозможно, вызовите функцию, должен всегда возвращать те же ценности, учитывая тот же вход. Как я избегаю этой ловушки?
Я думаю, что делают ваши расчеты, которые требуют случайных чисел внутри монада, что рефераты прочь генератор является самым чистым. Вот что будет выглядеть этот код:
мы собираемся поставить экземпляр 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
и это будет работать.
Список вы получаете из randoms
, бесконечно, и когда вы используете конечный префикс, вам нужно не выбрасывать бесконечный хвост. Вы можете пройти случайные числа в качестве дополнительного параметра и вернуть неиспользуемые случайные числа в качестве дополнительного результата, или вы можете припарковать бесконечную последовательность случайных чисел в монаде государства.
Аналогичная проблема возникает для компиляторов и других кодов, которые хотят подачи уникальных символов. Это просто настоящая боль в Haskell, потому что вы находитесь в состоянии потокового состояния (из генератора случайных чисел или генератора символов) по всему коду.
Я сделал рандомизированные алгоритмы как с явными параметрами, так и с монадом, и ни один действительно не удовлетворяет. Если вы Grok Monads, я, вероятно, возникнут небольшую рекомендацию, чтобы использовать государственный монад, содержащий бесконечный список случайных чисел, которые еще не использовались.
Вы также можете обойти эту проблему с использованием newstdgen
, а затем вы получите разное семя (практически) каждый раз.