我想在Haskell中实现以下(命令性)算法:
给定一系列对[(e0,s0),(e1,s1),(e2,s2),...,(en,sn)],其中“e”和“s”部分都是自然的数字不一定不同,在每个时间步骤中随机选择该序列的一个元素,假设(ei,si),并且基于(ei,si)的值,构建新元素并将其添加到序列中。
如何在Haskell中有效地实现这一点?随机访问的需要会使列表变得不好,而据我所知,一次添加一个元素的需要会使数组变得不好。
提前致谢。
答案 0 :(得分:12)
我建议使用Data.Set
或Data.Sequence
,具体取决于您的需要。后者特别为您提供对数索引查找(与列表的线性相反)和O(1)在任一端附加。
答案 1 :(得分:3)
“虽然需要一次添加一个元素会使数组变得不好”从算法来看,似乎你想要一个动态数组(又名矢量,数组列表等),它已经摊销了O(1)时间附加元素。我不知道它的Haskell实现,并且它不是一个非常“功能”的数据结构,但绝对有可能在Haskell中以某种状态monad实现它。
答案 2 :(得分:1)
如果你知道你将需要多少总元素,那么你可以创建一个这样大小的数组,首先是“稀疏”,然后根据需要你可以在其中放置元素。 下面的内容可用于表示这个新数组:
data MyArray = MyArray (Array Int Int) Int
(最后一个Int表示数组中使用了多少个元素)
答案 3 :(得分:1)
如果您真的需要停止并开始调整大小,您可以考虑使用simple-rope包以及StringLike
实例来使用Vector
之类的内容。特别是,这可能适用于从大型阵列开始并且对相对较少的添加感兴趣的场景。
也就是说,将单个元素添加到绳索的块中可能仍会导致大量复制。您将需要尝试您的具体案例,但您应该准备使用可变向量,因为您可能不需要纯粹的中间结果。
如果您可以一次性构建数组并且只需要您描述的索引行为,那么类似下面的内容就足够了,
import Data.Array.IArray
test :: Array Int (Int,Int)
test = accumArray (flip const) (0,0) (0,20) [(i, f i) | i <- [0..19]]
where f 0 = (1,0)
f i = let (e,s) = test ! (i `div` 2) in (e*2,s+1)
答案 4 :(得分:0)
从伊万姆那里得到一张纸条,我认为套装是这样做的。
import Data.Set as Set
import System.Random (RandomGen, getStdGen)
startSet :: Set (Int, Int)
startSet = Set.fromList [(1,2), (3,4)] -- etc. Whatever the initial set is
-- grow the set by randomly producing "n" elements.
growSet :: (RandomGen g) => g -> Set (Int, Int) -> Int -> (Set (Int, Int), g)
growSet g s n | n <= 0 = (s, g)
| otherwise = growSet g'' s' (n-1)
where s' = Set.insert (x,y) s
((x,_), g') = randElem s g
((_,y), g'') = randElem s g'
randElem :: (RandomGen g) => Set a -> g -> (a, g)
randElem = undefined
main = do
g <- getStdGen
let (grownSet,_) = growSet g startSet 2
print $ grownSet -- or whatever you want to do with it
这假定randElem
是从集合中选择随机元素的有效,可定义的方法。 (我问this SO question关于这种方法的有效实现)。在编写此实现时我意识到的一件事是它可能不适合您的需要,因为Set
s不能包含重复元素,并且我的算法无法为列表中多次出现的配对赋予额外的权重。