我不介意以「功能」的方式來完成。但我確實需要它是線性時間(而不是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