Я пытаюсь реализовать перестановку Фишера-Ятса некоторых данных. Этот алгоритм легко реализовать для одномерных массивов. Однако мне нужно иметь возможность перетасовывать данные в двумерной матрице.
Подход, который, как мне кажется, можно хорошо обобщить на массивы большей размерности, заключается в том, чтобы преобразовать мою матрицу произвольной размерности в одномерный массив индексов, перетасовать их, а затем реорганизовать матрицу, поменяв местами элемент с каждым индексом этого индексного массива с элементом с индексом элемента индексного массива. Другими словами, если взять матрицу 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
Затем у меня будет функция для выполнения перетасовки, которая выглядит так:
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
?Спасибо!