【发布时间】:2012-01-25 17:57:39
【问题描述】:
我正在尝试对某些数据实施 Fisher-Yates 洗牌。该算法对于一维数组很容易实现。但是,我需要能够在二维矩阵中打乱数据。
我认为可以很好地推广到高维数组的一种方法是将我的任意维度矩阵转换为一维索引数组,将它们打乱,然后通过交换该索引数组的每个索引处的元素来重组矩阵与索引数组元素的索引处的元素。换句话说,取一个 2x2 矩阵,例如:
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
然后我将有一个执行 shuffle 的函数,如下所示:
fisherYates :: (RandomGen g) => g -> Array Int b -> (Array Int b, g)
我的想法是(减去 RandomGen 管道)我应该能够像这样洗牌一个可洗牌的东西:
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?
谢谢!
【问题讨论】:
标签: haskell random functional-dependencies type-families