Algorithm 哈斯凯尔的纯克努斯/费舍尔·耶茨洗牌

Algorithm 哈斯凯尔的纯克努斯/费舍尔·耶茨洗牌,algorithm,haskell,functional-programming,shuffle,purely-functional,Algorithm,Haskell,Functional Programming,Shuffle,Purely Functional,在go中,我可以编写如下函数: func pureFisherYates(s []int, swaps []int) []int { newS := copy(s) for i, _ := range newS { for _, j := range swaps { newS[i], newS[j] = newS[j], newS[i] } } } 对我来说,这似乎是一个纯粹的函数。

在go中,我可以编写如下函数:

func pureFisherYates(s []int, swaps []int) []int {
    newS := copy(s)
    for i, _ := range newS {
            for _, j := range swaps {
                    newS[i], newS[j] = newS[j], newS[i]
            }
    }
}

对我来说,这似乎是一个纯粹的函数。对于相同的输入,它总是返回相同的输出,并且不会改变世界的状态(除非在某种严格意义上与任何其他函数相同,占用cpu资源,产生热能等)。然而,每当我寻找如何进行纯洗牌时,我会发现一些东西,比如,每当我专门寻找Haskell实现Fisher-Yates时,我要么得到一个用列表实现的
0^2
Fisher-Yates,要么得到一个
[a]->IO[a]
实现。是否存在一个
[a]->[a]
O(n)
shuffle,如果不存在,为什么我的上面的go实现是不纯的。

STmonad允许这种封装的可变性,并且包含可以在
ST
中变异的数组,然后在外部返回一个不可变的版本

给出了使用
ST
的Fisher-Yates shuffle的两个实现。它们不是字面上的
[a]->[a]
,但这是因为还需要处理随机数生成:

import System.Random
import Data.Array.ST
import Control.Monad
import Control.Monad.ST
import Data.STRef

-- | Randomly shuffle a list without the IO Monad
--   /O(N)/
shuffle' :: [a] -> StdGen -> ([a],StdGen)
shuffle' xs gen = runST (do
        g <- newSTRef gen
        let randomRST lohi = do
              (a,s') <- liftM (randomR lohi) (readSTRef g)
              writeSTRef g s'
              return a
        ar <- newArray n xs
        xs' <- forM [1..n] $ \i -> do
                j <- randomRST (i,n)
                vi <- readArray ar i
                vj <- readArray ar j
                writeArray ar j vi
                return vj
        gen' <- readSTRef g
        return (xs',gen'))
  where
    n = length xs
    newArray :: Int -> [a] -> ST s (STArray s Int a)
    newArray n xs =  newListArray (1,n) xs

但我不确定你是否能合理地称之为Fisher Yates shuffle。

这是一个有趣的事实集合。你的问题是什么?@DanielWagner希望我修正了它以使问题更清晰注意,在操作代码中,洗牌是由
交换::[Int]
参数驱动的,而不是随机性源,使原始函数变得纯粹。在Haskell中应该可以实现类似的功能。@chi很公平,添加了代码。虽然我不认为这个版本可以被称为Fisher-Yates shuffle…@AlexeyRomanov我有点像Haskell noob,所以我现在尽量避免深入研究语言扩展。最后一个使用swap而不使用
ScopedTypeVariables
的实现有什么方法吗?更不用说我能够在不了解ScopedTypeVariables在第一个实现中做了什么的情况下删除ScopedTypeVariables了place@Julian以前的版本()实际上没有它:
let newArray::Int->[a]->ST s(STArray s Int a);newArray n xs=newListArray(1,n)xs
然后
ar
import Control.Monad
import Control.Monad.ST
import Control.Monad.Random
import System.Random
import Data.Array.ST
import GHC.Arr

shuffle :: RandomGen g => [a] -> Rand g [a]
shuffle xs = do
    let l = length xs
    rands <- forM [0..(l-2)] $ \i -> getRandomR (i, l-1)
    let ar = runSTArray $ do
        ar <- thawSTArray $ listArray (0, l-1) xs
        forM_ (zip [0..] rands) $ \(i, j) -> do
            vi <- readSTArray ar i
            vj <- readSTArray ar j
            writeSTArray ar j vi
            writeSTArray ar i vj
        return ar
    return (elems ar)

*Main> evalRandIO (shuffle [1..10])
[6,5,1,7,10,4,9,2,8,3]
{-# LANGUAGE ScopedTypeVariables #-}

import Data.Array.ST
import Data.Foldable
import Control.Monad.ST

shuffle :: forall a. [a] -> [Int] -> [a]
shuffle xs swaps = runST $ do
    let n = length xs
    ar <- newListArray (1,n) xs :: ST s (STArray s Int a)
    for_ [1..n] $ \i ->
        for_ swaps $ \j -> do
            vi <- readArray ar i
            vj <- readArray ar j
            writeArray ar j vi
            writeArray ar i vj
    getElems ar