首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >haskell中的纯Knuth/Fisher-Yates混洗

haskell中的纯Knuth/Fisher-Yates混洗
EN

Stack Overflow用户
提问于 2019-06-19 13:58:54
回答 1查看 232关注 0票数 2

在go中,我可以像这样写一个函数:

代码语言:javascript
复制
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资源,产生热能等)。然而,每当我寻找如何做纯混洗时,我都会找到像this这样的东西,而每当我寻找特定的Haskell实现时,我要么得到一个使用列表实现的0^2 Fisher Yates,要么得到一个[a] -> IO [a]实现。是否存在[a] -> [a] O(n) shuffle ?如果不存在,为什么我上面的go实现不纯。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-06-19 15:48:24

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

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

代码语言:javascript
复制
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

代码语言:javascript
复制
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]

编辑:与Go代码一样,使用固定的swaps参数,代码非常简单

代码语言:javascript
复制
{-# 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

但我不确定你是否可以合理地将其称为Fisher-Yates shuffle。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56660901

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档