在我的OCaml程序中,我需要从一大串字符串中随机选择一个字符串。到目前为止,我尝试过两种不同的方法,每种方法都没有成功。我首先将所有字符串存储到列表中,然后从列表中随机选择一个元素:
let randomelement l =
List.nth l (Random.int (List.length l))
但如果选择列表中的第1000个字符串,则需要很长时间。所以我把它全部放到一个集合中,认为Set.choose
会从集合中返回一个随机元素。但这似乎并没有奏效。我想我有两个问题...... Set.choose
如何工作,是否有更好的方法可以随机选择Ocaml中的元素?
答案 0 :(得分:11)
如果您关心选择速度,则应使用其他容器。当你可以使用带有O(1)的数组,即恒定时间时,为什么使用带有O(n)访问权限或带有O(log n)设置的List。
调整您的示例:
let randomelement arr =
let n = Random.int (Array.length arr) in
Array.get arr n;;
答案 1 :(得分:4)
Set.choose
是Set.min_elt
的别名;虽然它可能不会在将来。
List.nth
肯定会很糟糕。
数组效果很好,但是如果你需要添加更多元素或删除元素,这可能是一本记账的噩梦。
查看Random Access Lists的实现,它们具有非常适合插入,删除,查找和基数的时间,而不受数组约束。
当我最初遇到此问题时,我将Set
和Map
的实施扩展为包括random
和nth
。要扩展模块,您需要重新实现结构并添加标识函数以在两者之间进行转换。我写了一个名为XSet
的新模块,其中包含以下样板:
module Make (Ord : Set.OrderedType) =
struct
include Set.Make(Ord)
type impl = Empty | Node of impl * elt * impl * int
external impl_of_t : t -> impl = "%identity"
external t_of_impl : impl -> t = "%identity"
...
end
你必须使用impl_of_t
反之亦然来调用普通的Set
函数,或者从传递的参数中调用你的个人函数 - 传递给你的函数的应该是Set.Make
的实施t
不是impl
。
答案 2 :(得分:3)
不,Set.choose是确定性的,我认为它已在文档中指定。我认为在当前的实现中它返回最小元素。
在哪个数据结构中首先存储了您的字符串集?获取随机元素的一种简单方法是计算您拥有的不同字符串的数量,在中间选择一个随机数K,并获得您拥有的“第K个字符串”。对于某些数据结构,这可以相当有效地完成(例如对于数组,它是一个恒定的时间操作),对其他数据结构效率较低。
答案 3 :(得分:3)
您的问题意味着您总是希望准备包含字符串的结构,然后,您将随机选择几次而不更改集合。
如果这是正确的,那么我建议将它们存储在一个数组中。这样可以最快地访问随机元素(一旦选择了随机索引就直接访问)。一旦选择了随机索引i,集合和列表实现都很难访问第i个元素(尽管对于集合,如果你不介意随机性差的随机选择,你可以在二叉树中选择一个随机路径代表集合。你不能从实现之外做到这一点,但你可以复制粘贴它并在模块中添加你的函数。)
答案 4 :(得分:2)
首先,有一个List.nth函数可以替换你的帮助函数。
let n = Random.int (List.length lst) in
List.nth n lst ;;
(虽然这不会更快,因为我很确定它和你的助手功能完全一样)
就加速这个问题而言:如果您拥有的字符串数量是固定的,那么您可以使用一个数组,这可以加快速度,因为数组的访问时间是恒定的。
答案 5 :(得分:1)
Set.choose
保证每次为给定集合选择相同的元素。它没有指定选择哪个元素,但是如果你查看它的源代码,你会看到它返回最小元素。
您最好的选择是使用阵列。如果你想要一个不可变的数据结构,那么我建议用一个整数键映射。