我不介意以“功能”的方式来完成。但我确实需要它是线性时间(而不是O(n log n)),并且我更喜欢类型签名保持完整(即不添加其他类型约束)。这是我到目前为止,但我不断收到一个堆栈溢出:随机排列大型列表(超过1亿个元素)
import Control.Monad
import Control.Monad.ST
import Data.Array.ST
import Data.STRef
import System.Random
randomPermute :: RandomGen g => [a] -> g -> ([a],g)
randomPermute l rgen = runST $ newListArray (1,n) l >>= body rgen where
n = length l
body :: RandomGen g => g -> STArray s Int e -> ST s ([e],g)
body rgen arr = do
rgenRef <- newSTRef rgen
let pick i j = do vi <- readArray arr i
vj <- readArray arr j
writeArray arr j vi
return vj
rand lo hi = do rgen <- readSTRef rgenRef
let (v,rgen') = randomR (lo,hi) rgen
writeSTRef rgenRef rgen'
return v
rv <- forM [1..n] $ \i -> do
j <- rand i n
pick i j
rgen <- readSTRef rgenRef
return (rv,rgen)
ascCount x = sum $ map oneIfBig $ zip x $ tail x where
oneIfBig (x,y) = if x<y then 0 else 1
main = do
-- Using String types just for testing
res <- getStdRandom $ randomPermute $ map show [1..1000000]
putStrLn $ show $ ascCount res
现在我用命令式语言打交道告诉我,应该避免使用堆栈一起的方式。但在Haskell中,我似乎无法弄清楚如何。我发现了一些方法,如果我使用unboxed数组。但正如我所说,我不想添加额外的限制。有任何想法吗?
编辑:我也很感激,如果有人可以向我解释上面的代码是如何消耗堆栈空间,以及为什么我不能简单地避免使用尾递归调用。我尝试在某些地方使用急切的评估,但它并没有帮助
谢谢。但是这难道不会将问题转换为生成整数置换的问题吗?如果我理解正确,你的软件包(mersenne-random,vector-random等)不会导出任何生成具有非重复元素的向量的方法。 由于我对haskell比较新,我还想知道GHC运行时如何在我粘贴的代码中使用堆栈空间,以便我不会再犯同样的错误 – Samee
它将问题分解为O( n)组件来执行置换,并且O(n log n)步骤来生成唯一的随机数(通过集合的一个集合) –
啊,所以我们回到O(n log n)。好,谢谢。但我们可以避免这种情况吗?只是好奇 – Samee