我試圖實現Fisher-Yates洗牌的一些數據。這個算法對於一維數組很容易實現。但是,我需要能夠在二維矩陣中混洗數據。Haskell:沒有函數依賴關係的洗牌數據
一種我認爲可以很好地推廣到更高維數組的方法是將我的任意維度的矩陣轉換爲一維索引數組,然後對這些數組進行混洗,然後通過交換此索引的每個索引處的元素來重新組織矩陣數組與索引數組元素的索引處的元素。換句話說,取2×2矩陣例如:
1 2
3 4
我會轉換成這「陣列」這樣的:
[(0, (0,0)), (1, (0,1)), (2, ((1,0)), (3, (1,1))]
此我會然後加擾每正常成,比方說,
[(0, (1,0)), (1, (0,1)), (2, ((1,1)), (3, (0,0))]
一旦重組,原來的矩陣將變成:
2 3
4 1
我這裏基本方法是,我想有一個類型類,看起來是這樣的:
class Shufflable a where
indices :: a -> Array Int b
reorganize :: a -> Array Int b -> a
然後,我將有一個函數來執行它看起來像這樣的洗牌:
fisherYates :: (RandomGen g) => g -> Array Int b -> (Array Int b, g)
的想法是,(減去RandomGen管道),我應該能夠洗牌shuffleable的事情,像這樣:
shuffle :: (Shufflable a, RandomGen g) => a -> g -> (a, g)
shuffle array = reorganize array (fisherYates (indices array))
這是我到目前爲止有:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}
module Shuffle where
import Data.Array hiding (indices)
import System.Random
fisherYates :: (RandomGen g) => Array Int e -> g -> (Array Int e, g)
fisherYates arr gen = go max gen arr
where
(_, max) = bounds arr
go 0 g arr = (arr, g)
go i g arr = go (i-1) g' (swap arr i j)
where
(j, g') = randomR (0, i) g
class Shuffle a b | a -> b where
indices :: a -> Array Int b
reorganize :: a -> Array Int b -> a
shuffle :: (Shuffle a b, RandomGen g) => a -> g -> (a, g)
shuffle a gen = (reorganize a indexes, gen')
where
(indexes, gen') = fisherYates (indices a) gen
instance (Ix ix) => Shuffle (Array ix e) ix where
reorganize a = undefined
indices a = array (0, maxIdx) (zip [0..maxIdx] (range bound))
where
bound = bounds a
maxIdx = rangeSize bound - 1
swap :: Ix i => Array i e -> i -> i -> Array i e
swap arr i j = arr // [ (i, i'), (j, j') ]
where
i' = arr!j
j' = arr!i
我的問題:
- 我覺得這是解決一個簡單的問題,很多語言擴展。會更容易理解或寫另一種方式?
- 我覺得社區正朝着功能依賴關係的類型家庭發展。有沒有辦法使用它來解決這個問題?
- 我的一部分想知道是否
fisherYates
功能,可不知何故搬進Shuffle
類型類。有沒有可能和/或值得做的設置此,讓你無論是實現shuffle
或同時實現indices
和reorganize
?
謝謝!
謝謝!我以前沒有遇到過修理,這將非常有幫助。 – 2011-12-22 04:16:08