Sto provando ad implementare un rimescolamento di alcuni dati da parte di Fisher-Yates. Questo algoritmo è facile da implementare per gli array monodimensionali. Tuttavia, ho bisogno di essere in grado di mescolare i dati in una matrice bidimensionale.Haskell: mischiare dati senza dipendenze funzionali
Un approccio che a mio avviso potrebbe generalizzare meglio ad array di dimensioni superiori è convertire la matrice arbitrariamente dimensionata in una matrice di indici unidimensionale, mescolarli e quindi riorganizzare la matrice scambiando l'elemento in ciascun indice di questo indice array con l'elemento nell'indice dell'elemento dell'array di indice. In altre parole, di prendere una matrice 2x2 quali:
1 2
3 4
Vorrei convertirlo in questa "matrice":
[(0, (0,0)), (1, (0,1)), (2, ((1,0)), (3, (1,1))]
Questa avrei poi scramble per normale in, diciamo,
[(0, (1,0)), (1, (0,1)), (2, ((1,1)), (3, (0,0))]
volta riorganizzata, matrice origine diventerebbe:
2 3
4 1
Il mio approccio di base è che voglio avere una classe tipo che sembra qualcosa di simile:
class Shufflable a where
indices :: a -> Array Int b
reorganize :: a -> Array Int b -> a
Allora dovrò una funzione per eseguire la riproduzione casuale che assomiglia a questo:
fisherYates :: (RandomGen g) => g -> Array Int b -> (Array Int b, g)
il pensiero è che (meno la randomgen impianto idraulico) dovrei essere in grado di mescolare una cosa shuffleable in questo modo:
shuffle :: (Shufflable a, RandomGen g) => a -> g -> (a, g)
shuffle array = reorganize array (fisherYates (indices array))
Ecco quello che ho finora:
{-# 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
I miei problemi:
- Mi sento come questo è un sacco di estensioni del linguaggio per risolvere un problema semplice. Sarebbe più facile capire o scrivere in un altro modo?
- Mi sento come se la comunità si stesse spostando verso famiglie di tipi su dipendenze funzionali. C'è un modo per usarlo invece per risolvere questo problema?
- Una parte di me si chiede se la funzione
fisherYates
possa essere spostata in qualche modo nella classe di caratteriShuffle
. Sarebbe possibile e/o vale la pena farlo per impostare questo in modo da implementareshuffle
o implementare entrambiindices
ereorganize
?
Grazie!
Grazie! Non ho eseguito in precedenza repa, sarà molto utile. –