如何在Haskell中在没有IO的情况下生成随机数? [英] How can I generate random numbers in Haskell without IO in a range?

查看:67
本文介绍了如何在Haskell中在没有IO的情况下生成随机数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想生成一个范围内的随机数,并且类型签名为 Int->.Int .我已经阅读了其他多篇文章,但是没有一篇文章提出了返回 Int 类型的建议方法.我在代码中使用了 System.IO.Unsafe ,但不建议这样做.这是我的代码:

I would like to generate random numbers in a range and the type signature to be Int -> Int. I've read multiple other posts but none of them suggested ways to return a type Int. I used System.IO.Unsafe in my code but it is not recommended to do so. Here's my code:

import System.IO.Unsafe

-- random number generator
rng :: Int -> Int
rng upper = unsafePerformIO $ randomRIO (0,upper-1) 

有人对在Haskell范围内如何生成随机Int有何建议?

Does anyone have any suggests on how to generate random Int in a range in Haskell?

可能无法更改 IO Int->Int ,所以我将代码转换为

It might be impossible to change IO Int -> Int so I converted my code to

-- random number generator
rng :: Int -> IO Int
rng upper = randomRIO (0,upper-1) 

之所以需要rng是因为我想获取列表范围内的随机数,以获得列表元素的索引.

The reason why I need a rng is because I want to get random numbers within the range length of the list to get an index for an element of a list.

列表!rng(长度列表),但出现错误无法将预期的类型"Int"与预期的实际类型"IO Int" 匹配.

这不是重复项,因为1.我想要一个范围内的值,2.我的rng不返回相同的值.我是Haskell的新手,我不知道如何操作Monads.感谢您的帮助.

It's not a duplicate because 1. I want values in a range, 2. my rng does not return the same values. I'm new to Haskell and I don't know how to manipulate Monads. Any help is appreciated.

推荐答案

我本着 https://xkcd.com的精神/221/,这是一个没有任何IO的解决方案":

I the spirit of https://xkcd.com/221/, here's a "solution" without any IO:

rng :: Int -> Int
rng upper
 | upper<=4   = upper
 | otherwise  = 4

因此,您可以得到一个符合RFC 1149.5的随机数".除非超出范围,否则总是4.

So that gives you an "RFC 1149.5 compliant random number". It's always four, unless that is outside the range.

这是什么问题?好吧,很明显,它总是给相同的数字 –所以必须为,因为所有的Haskell函数必须为相对透明.OTOH,随机数生成器应该在您每次调用它时赋予不同的编号...因此,它不是 一个函数,并且大多数其他编程语言只是假装它是具有副作用的函数 –因为它们没有适当的方式来表示副作用是什么.好吧,Haskell确实有一种适当的表达方式,这就是IO monad:您可以进行依赖于副作用的计算,但是显然,如果运行这些计算,它们本身将具有副作用.
因此,签名 Int->IO Int 对于该功能确实有意义.(此函数,但是结果是 IO操作,只有执行该操作会为您提供 Int )

What's the problem with this? Well, clearly it gives always the same number – and so it must be, because all Haskell functions must be functions, i.e. referentially transparent. OTOH, a random number generator is supposed to give different number each time you call it... it is thus not a function, and most other programming languages merely pretend it is a function with side-effect – because they have no proper means to express what side-effects are. Well, Haskell does have a proper means of expressing that, and it is the IO monad: you can have computations that depend on a side effect, but clearly these computations if you run them will then have that side-effect themselves.
In that light, the signature Int -> IO Int does make sense for the function. (This is a function, but the result is an IO action and only executing that action gives you an Int.)

丑陋的是 IO Int 可以在 IO 中执行任何操作 –例如,它可以 PRNG ,它们会保持恒定大小的状态变量,每次提取一个值时都会以一种随机的方式进行更新.下次,此状态将有所不同,因此您将根据需要获得不同的值.此状态变量可以保留在 IO 可变位置

What's ugly about that is that IO Int could literally do anything in IO – it could, for instance, launch some missiles and give you back the number of casualities. More realistically, it could easily modify some file in you home directory. Whereas what you want is actually just a teeny tiny harmless side-effect, just enough to produce a new random number the next time. Usually, random number generators are anyways not really random but PRNGs, which keep a constant-size state variable that is updated in a random-looking way each time you pull a value. The next time, this state will be different and thus you get a different value, as desired. This state variable could be held in an IO-mutable location

import Data.IORef

type RandStV = Int
type RandSt = IORef RandStV

rng' :: RandSt -> Int -> IO Int
rng' rSt upper = do
   x <- readIORef rSt
   let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff -- https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/random_r.c;hb=glibc-2.26#l362
   writeIORef rSt x'
   return $ x `mod` upper

...或者,您可以明确地将更新的状态与结果一起传递

...or, you could just explicitly pass the updated state along with the result

rng'' :: Int -> RandStV -> (RandStV, Int)
rng'' upper x =
   let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff
   in (x', x `mod` upper)

...或者可以在专用的

...or it could be passed around in a dedicated state monad, which is just another way of writing the passing-on of an updated variable:

type RandStM = State RandStV

rng''' :: Int -> RandStM Int
rng''' upper = do
   x <- get
   let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff
   put x'
   return $ x `mod` upper

请参阅 random-fu软件包,以获取有关此类 random的有用帮助方法monad .

See the random-fu package for useful helpers on such a random monad.

一种解释 rng'''的数学方法是说它是一个以上限为参数并返回生成一个整数表示您正在从分布中进行采样.

One mathematical way to interpret rng''' is to say it is a function that takes an upper bound as the argument and gives you back a distribution of numbers. The distribution is always the same, but it "contains" many numbers together with the probability of them occuring. Actually generating an integer means you're sampling from the distribution.

这篇关于如何在Haskell中在没有IO的情况下生成随机数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆