懒惰地生成随机访问数据结构的最佳技术是什么?

时间:2011-02-10 02:54:41

标签: haskell

在Haskell中,我想生成一个未确定长度的随机整数列表。 (但是,不到100万。)

我不太可能立即需要列表中的所有元素,所以我想懒得生成它。但是,一旦生成,我将需要使用随机访问来访问列表中的元素。所以,我认为最好的技术是将无限列表复制到数组中。但是,我不知道数组是否可以非常容易地与列表“互换” - 例如,一旦我想生成列表中的更多项目,我想从我离开的地方开始但扩展数组。

无论如何,也许我应该解决一些log(n)树结构而不是数组?你觉得怎么样?

Haskell是否具有可以使用类似列表的API访问的顺序元素的树结构?

5 个答案:

答案 0 :(得分:7)

如果你有一个好的PRNG,那么用接近的种子生成的值应该是独立的,所以你可以选择一个初始种子,然后数组中的每个单元格i将是{{1 }}

这基本上只是将它用作散列函数:P

如果你这样做,你甚至不需要数组,你可以只有一个函数prng(seed+i)。这是否比一系列惰性值更好将取决于您的PRNG的复杂性,但无论哪种方式,您都将获得O(1)访问。

答案 1 :(得分:4)

这似乎可能有用,但随着列表变长,你需要生成越来越大的树:http://www.codeproject.com/KB/recipes/persistentdatastructures.aspx#RandomAccessLists(虽然它是一个log(n)结构)。像http://research.janelia.org/myers/Papers/applicative.stack.pdf这样的东西也可能有用,但是当动态生成列表时,我没有看到如何使它有效地工作(使用共享结构)。

一种可能的方法是使用伪随机数生成器,该生成器允许在跨线性时间内“跳过”或跳过n步。一种方法(速度慢但在恒定时间内工作)是在计数器模式下使用分组密码;见http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives。有关详细信息,请参阅http://cs.berkeley.edu/~mhoemmen/cs194/Tutorials/prng.pdf

答案 2 :(得分:2)

不是答案,而是一个考虑因素:请注意,用于生成随机值列表的任何标准技术都将涉及通过种子进行线程化。因此,如果我生成一个惰性随机无限列表并强制第100个元素的值,这不仅会强制cons单元格的脊柱0-99,还会强制它们的值。

无论如何,我建议使用懒惰的可存储载体:http://hackage.haskell.org/package/storablevector

你仍然有O(n)访问权限,但你已经减少了你的块大小因素,所以实际上这比普通列表要快得多。你仍然可以获得模块大小,你感兴趣的懒惰属性。

答案 3 :(得分:0)

Data.Sequence.Seq怎么样?它不是懒惰的,但它确实支持O(1)追加到两端,查找是O(log(min(i,n-i))),这应该比大多数其他树结构更好。您可以保留Seq并根据需要生成/附加更多输出。

如果您偏好Seq提供的API,那么还有ListLike的实例。

答案 4 :(得分:0)

您可以做的一件事是创建一个数组列表。因此,如果每个数组存储序列的M元素,则访问元素nO(n/M)。 这可能是一个很好的中间立场。

例如:

import Data.Array
import Data.List

fibsList :: (Num a) => [a]
fibsList = 1 : 1 : zipWith (+) fibsList (tail fibsList)

chunkSize :: (Num a, Ix a) => a
chunkSize = 100000

fibsChunks :: (Num i, Ix i, Num e) => [Array i e]
fibsChunks = mkChunks fibsList
  where mkChunks fs = listArray (0,chunkSize-1) xs : mkChunks ys
          where (xs,ys) = splitAt chunkSize fs

lookupFib :: (Integral i, Ix i, Num e) => i -> e
lookupFib n = fibsChunks `genericIndex` q ! r
  where (q,r) = n `divMod` chunkSize