Haskell Random cannot build an infinite type: a1 = IO a1

I want to create a list of 26 random numbers, the sum of which is 301 using Haskell. I am writing the following:

import System.Random f 1 sum = [sum] fn sum = m : (f (n-1) (sum-m)) where m = randomRIO (0,sum) 

but it cannot be compiled! I'm confused 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': fn sum = m : (f (n - 1) (sum - m)) where m = randomRIO (0, sum) 
+4
source share
4 answers

In this case, the error message is somewhat confusing, but in perfolin you need to work in the IO monad, since it uses randomRIO , which is located in IO , and there is (by design) no way to run the IO code from the << 20> code.

 f 1 sum = return [sum] fn sum = do x <- randomRIO (0, sum) xs <- f (n - 1) (sum - x) return (x : xs) 
+8
source

Unlike what the hammer wrote, the error message becomes much clearer if you write the type expected for the function f :

 f :: Int -> Int -> [Int] f 1 sum = [sum] fn sum = m : (f (n-1) (sum-m)) where m = randomRIO (0,sum) 

gives an error:

 Couldn't match expected type `Int' with actual type `IO Int' In the first argument of `(:)', namely `m' In the expression: m : (f (n - 1) (sum - m)) In an equation for `f': fn sum = m : (f (n - 1) (sum - m)) where m = randomRIO (0, sum) Failed, modules loaded: none. 

Which pretty much tells you exactly what is wrong, that is, m is of type IO Int , not Int

+5
source

As others have pointed out, your algorithm will not produce evenly distributed output.

Easy way to get uniform output:

  • Generate n-1 random numbers ranging from 0 to sum (inclusive)
  • Insert 0 and sum in the list of random numbers
  • Sort the resulting list
  • Returns a list of differences between consecutive values ​​in a sorted list.

Example:

  • Suppose we need four integers with the sum of 100, we request three random values ​​from the RNG and get [72,33,43]
  • We insert 0 and 100 and sort the list, providing [0,33,43,72,100]
  • We calculate the differences [33-0, 43-33, 72-43, 100-72]
  • The result will be [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) 

As an example, you can name it as randomsWithSum 26 (301::Int)

The same goes for floating point types, for example. randomsWithSum 4 (1::Double)


Change The arguments have changed, so 26 `randomsWithSum` 301 does what its name suggests.

+5
source

Following the demas comment, I tried to tweak your algorithm. We probably want each of the numbers n be β€œthe same” as everyone else, so we just try until we get the right amount. Perhaps there is a better way.

 -- f 0 rng = return [] -- fn rng = randomRIO (0,rng) >>= (\x-> fmap (x:) $ f (n-1) rng) gn sumval = let s = 2*sumval `div` n -- expected value upto z is probably z/2, hi = do -- if all are equally likely xs <- sequence $ replicate n (randomRIO (0,s)) if sum xs == sumval then return (xs, i) -- i is number of attempts else h (i+1) in h 1 -- test: Prelude System.Random> g 26 301 ([15,23,15,0,13,8,23,11,13,19,5,2,10,19,4,8,3,9,19,16,8,16,18,4,20,0],2) Prelude System.Random> g 26 301 ([20,14,3,6,15,21,7,9,2,23,22,13,2,0,22,9,4,1,15,10,20,7,18,1,18,19],12) Prelude System.Random> g 26 301 ([4,3,4,14,10,16,20,11,19,15,23,18,10,18,12,7,3,8,4,9,11,5,17,4,20,16],44) Prelude System.Random> g 26 301 ([6,6,22,1,5,14,15,21,12,2,4,20,4,9,9,9,23,10,17,19,22,0,10,14,6,21],34) Prelude System.Random> g 26 301 ([20,9,3,1,17,22,10,14,16,16,18,13,15,7,6,3,2,23,13,13,17,18,2,2,8,13],169) 
0
source

Source: https://habr.com/ru/post/1443603/


All Articles