Test.QuickCheck没有正确生成随机值

时间:2014-02-14 00:52:15

标签: haskell

我正在使用Test.QuickCheck生成随机的数独谜题。

data Sudoku = Sudoku { getSudoku :: [[Maybe Int]] } deriving (Show, Eq)

rows :: Sudoku -> [[Maybe Int]]
rows (Sudoku rs) = rs

--B1
printSudoku :: Sudoku -> IO ()
printSudoku s = do
    putStrLn . unlines . map (map (maybe '.' (head . show))) $ rows s

--C1
cell :: Gen (Maybe Int)
cell = suchThatMaybe (frequency [(90, choose (0,0)),(10, choose(1,9))]) (/=0)

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

--C3
prop_Sudoku :: Sudoku -> Bool
prop_Sudoku = isSudoku

checkRandomSudoku :: IO [Bool]
checkRandomSudoku = do
    s <- sample' (arbitrary :: Gen Sudoku)
    return $ map isSudoku s

代码运行正常。但是,当我执行

a&lt; - sample'(任意:: Gen Sudoku)

序列$ map printSudoku a

它会返回如下内容:

....5..3.
...4.....
...2.....
...5.....
.........
...33....
...5.....
...2.4...
.........

.........
.343.....
.........
......9.2
.........
45....5.1
.2......7
.7..88.34
.9....6..

....2..8.
.2121638.
.7.7...9.
4..45.6..
.....6.2.
..6.6....
53..9.6..
..9....7.
.47892...

.373411..
5...3282.
...45..9.
8989..18.
31.8113..
9..35.6..
4.685....
.4....39.
7..6.5.76

48.178.53
1.871.4.4
3165.17..
.1...7.59
.98126.51
6.6...775
9.4636952
.5..239..
372.....8

.34.73129
.5.8.27.1
344.34931
28.6.94.1
6327.3..8
3743.5496
93...7984
..82.8...
..3.54.93

273847853
5568.7465
832.73515
3766..6.7
.7.196256
1.96.9.3.
.7156.268
1615.196.
.392..633

731652284
863.8.768
31..5.5.6
961.5.467
1245.1159
5..275471
52.727759
6.656.849
99.72352.

这显然不是随意的。空单元的分布最初非常高,然后缓慢下降。我使用错误的功能还是错误的方式?感谢

1 个答案:

答案 0 :(得分:6)

您看到此行为的原因是QuickCheck会逐渐尝试更大的测试用例。这在手册的resizing部分进行了描述。

suchThatMaybe增加大小的情况下,它会重试生成一个任意值来匹配传递给它的谓词。你可以在源http://hackage.haskell.org/package/QuickCheck-2.6/docs/src/Test-QuickCheck-Gen.html#suchThatMaybe中看到它。有趣的代码是:

try _ 0 = return Nothing
try k n = do x <- resize (2*k+n) gen
             if p x then return (Just x) else try (k+1) (n-1)

正在发生的事情是suchThatMaybe重试nn取决于传递给生成器的size参数。

sample' tries the sizes [0,2..20]一直传播到suchThatMaybe

您可以通过调用生成器上的resize来覆盖不断增加的大小:

>>> a <- sample' $ (resize 2 arbitrary :: Gen Sudoku)
>>> sequence $ map printSudoku a
.....8.5.
.......4.
.........
4..25...5
......9..
....5....
........7
.....7...
...4.....

...5.....
.......4.
...6...6.
..14.....
...7...7.
....2....
.....6...
...4..572
.4.....6.

..6..8...
..4......
.........
......9..
839......
67..4....
.5.......
....5....
........3

4...1....
7..9.39..
....6....
...4.1...
.........
.........
..9....6.
.2.9...84
.....8...

.64......
..3.44...
4.......4
....1..8.
.9.......
34.......
.....6...
18.2..593
.4.7.....

.........
...8..6..
.2......5
...5.....
.2.......
........6
.3....13.
8.1.2..85
....5....

..7......
..67...5.
..6......
27....1.9
.9.......
78.....7.
......34.
.......2.
..81...81

3.1......
.........
.....6...
.........
.16.71...
.........
.2.......
.........
.....9.1.

..65..6.9
........5
..1.4....
....86...
.2..2..2.
.....9...
..6......
.........
...7..855

.......94
...14..8.
.....4...
...3....9
.........
.....5...
.5.......
45.....8.
..48.....

4........
......3..
5......4.
.4..6..2.
..3......
.........
..9......
6..9.....
....7....