2014-02-14 54 views
4

我正在使用Test.QuickCheck生成隨機數獨謎題。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 

該代碼運行良好。然而,當我執行

一個< - 樣本」(任意::創數獨)

序列$地圖printSudoku一個

返回是這樣的:

....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. 

這顯然是根本不是隨機的。空細胞的分佈開始時非常高,然後緩慢下降。我是否使用了錯誤的功能或以錯誤的方式使用?謝謝

+0

作爲你的做法評論(而不是你目前的問題的說明):我覺得這不是一個好主意。生成正確的數獨謎題似乎是其中一種聽起來很容易但不是的東西,我敢打賭,一旦解決了這個問題,你會遇到十幾個奇怪的問題。但在野外已經有很多拼圖 - 你可以通過使用現有的語料庫來跳過所有的問題嗎? –

+0

我只是繼續[在此](http://www.cse.chalmers.se/edu/year/2013/course/TDA555/lab3.html)發現的練習。我有點同意你的看法。在我的例子中,你可以看到它甚至沒有檢查行/列/框中是否有重複的數字,但我仍然想知道爲什麼它不會像我期望的那樣生成隨機數。特別是頻率,我會期待更多空缺項目,因爲我擁有更多的權重比編號條目 – dtan

回答

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) 

發生了什麼事就是n取決於傳遞給發電機的大小參數suchThatMaybe重試n倍。

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.... 
+0

我明白了。我認爲這很不直觀,因爲關於hackage函數的描述並不表示它影響謂詞的概率。我很想知道,如果不使用調整大小,那麼可以使用這種可能的方法?如果你會如此善良,你能讓我舉個例子嗎?謝謝 – dtan

+0

'suchThatMaybe'的想法是覆蓋儘可能多的不同測試用例。所以如果你有一個很少被滿足的謂詞,它將更有可能通過重試達到一個正面的測試用例。 –