Pro9
Pro9

Reputation: 165

Randomly generate a weighted number in haskell

I'm new to Haskell and I am trying to generate an random weighted number for an arbitrary cell in a Sudoku

90% of the time, it should generate Nothing
10% of the time, it should generate a random value between 1 and 9

This is what I have came up with so far (it does not compile)

-- cell generates an arbitrary cell in a Sudoku
-- cell :: Gen (Maybe Int)


cell = case x of 1 -> return (Just y); _ -> return Nothing
        where std = mkStdGen(100)
              (x, a) = randomR(1, 10) std
              (y, a')= randomR(1, 9) std

Any help in getting it to compile, or by pointing me towards a better approach is much appreciated


Using Quickcheck this is how I did it:

-- cell generates an arbitrary cell in a Sudoku
cell :: Gen (Maybe Int)
cell = frequency [(9, return Nothing), 
               (1, do r <- choose (1,9); return (Just r))]        

-- an instance for generating Arbitrary Sudokus
instance Arbitrary Sudoku where
  arbitrary =
    do rows <- sequence [ sequence [ cell | j <- [1..9] ] | i <- [1..9] ]
       return (Sudoku rows)

Upvotes: 1

Views: 369

Answers (1)

user2407038
user2407038

Reputation: 14598

If you are using QuickCheck, you certainly don't want a cell to a pure value - randomR (0,100) (mkStdGen 43434343) is not random whatsoever, so it will be useless to you for testing.

Since you are using QuickCheck, you need to use QuickCheck functions. You cannot easily use System.Random nor should you. Simply use the (quite powerful) QuickCheck API, which has a function choose :: Random a => (a,a) -> Gen a, which picks a random number in the range, and a function frequency :: [(Int, Gen a)] -> Gen a which selects a random generator, based on its weight, ie the likelihood, and returns that generator. You can write cell as:

cell :: Gen (Maybe Int)
cell = frequency 
  [ (1, Just `fmap` choose (1,9))
  , (9, return Nothing) 
  ]

Then, for example, if you define your Sudoku board as data Sudoku = Sudoku [[Maybe Int]], you can easily write an Arbitrary instance for it using cell:

import Control.Monad (replicateM)

instance Arbitrary Sudoku where 
  arbitrary = Sudoku `fmap` replicateM 9 (replicateM 9 cell)

Upvotes: 2

Related Questions