I want to generate a list with 26 random integers which sum is 301 with Haskell. I write the following:
import System.Random
f 1 sum = [sum]
f n sum = m : (f (n-1) (sum-m))
where m = randomRIO (0,sum)
but it can't be compiled! I am confused with IO!
Occurs check: cannot construct the infinite type: a1 = IO a1
In the first argument of `(:)', namely `m'
In the expression: m : (f (n - 1) (sum - m))
In an equation for `f':
f n sum
= m : (f (n - 1) (sum - m))
where
m = randomRIO (0, sum)
The error message is somewhat confusing in this case, but the punchline is that you need to work in the IO
monad, since it's using randomRIO
which is in IO
, and there is (by design) no way to run IO
code from non-IO
code.
f 1 sum = return [sum]
f n sum = do
x <- randomRIO (0, sum)
xs <- f (n - 1) (sum - x)
return (x : xs)
As others have pointed out, your algorithm will not give uniformly-distributed output.
An easy way to get uniform output is:
n-1
random numbers in the range from 0
to sum
(inclusive)0
and sum
into the list of random numbersExample:
[72,33,43]
0
and 100
and sort the list, giving [0,33,43,72,100]
[33-0, 43-33, 72-43, 100-72]
[33,10,29,28]
In Haskell:
randomsWithSum :: (Num n, Ord n, Random n) => Int -> n -> IO [n]
randomsWithSum len sum =
do b <- sequence $ take (len-1) $ repeat $ randomRIO (0,sum)
let sb = sort (sum:b) in
return $ zipWith (-) sb (0:sb)
For your example you would call this as randomsWithSum 26 (301::Int)
The same applies to floating-point types, e.g. randomsWithSum 4 (1::Double)
Edit Swapped the arguments, so that 26 `randomsWithSum` 301
does what its name suggests.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With