如何从集合中随机选取一个元素? 我特别感兴趣的是从a中随机选取一个元素 Java中的HashSet或LinkedHashSet。 也欢迎其他语言的解决方案。


当前回答

Icon有一个集合类型和一个随机元素操作符,一元“?”,因此表达式

? set( [1, 2, 3, 4, 5] )

将产生1到5之间的随机数。

当程序运行时,随机种子被初始化为0,因此要在每次运行时产生不同的结果,请使用randomize()

其他回答

不幸的是,这在任何标准库集合容器中都不能有效地完成(比O(n)更好)。

这很奇怪,因为向哈希集和二进制集添加随机选择函数是非常容易的。在一个非稀疏散列集中,你可以尝试随机的条目,直到你得到一个命中。对于二叉树,您可以在左子树或右子树之间随机选择,最多有O(log2)步。我已经实现了后面的演示如下:

import random

class Node:
    def __init__(self, object):
        self.object = object
        self.value = hash(object)
        self.size = 1
        self.a = self.b = None

class RandomSet:
    def __init__(self):
        self.top = None

    def add(self, object):
        """ Add any hashable object to the set.
            Notice: In this simple implementation you shouldn't add two
                    identical items. """
        new = Node(object)
        if not self.top: self.top = new
        else: self._recursiveAdd(self.top, new)
    def _recursiveAdd(self, top, new):
        top.size += 1
        if new.value < top.value:
            if not top.a: top.a = new
            else: self._recursiveAdd(top.a, new)
        else:
            if not top.b: top.b = new
            else: self._recursiveAdd(top.b, new)

    def pickRandom(self):
        """ Pick a random item in O(log2) time.
            Does a maximum of O(log2) calls to random as well. """
        return self._recursivePickRandom(self.top)
    def _recursivePickRandom(self, top):
        r = random.randrange(top.size)
        if r == 0: return top.object
        elif top.a and r <= top.a.size: return self._recursivePickRandom(top.a)
        return self._recursivePickRandom(top.b)

if __name__ == '__main__':
    s = RandomSet()
    for i in [5,3,7,1,4,6,9,2,8,0]:
        s.add(i)

    dists = [0]*10
    for i in xrange(10000):
        dists[s.pickRandom()] += 1
    print dists

我得到[995,975,971,995,1057,1004,966,1052,984,1001]作为输出,所以分布很好。

我自己也遇到过同样的问题,但我还不确定这种更有效的选择所带来的性能收益是否值得使用基于python的集合。当然,我可以将其提炼并翻译成C语言,但这对我来说工作量太大了:)

你知道吗?

在java.util.Collections中有一些有用的方法用于洗牌整个集合:collections .shuffle(List<?>)和collections .shuffle(List<?>列表,随机rnd)。

既然你说“其他语言的解决方案也欢迎”,下面是Python的版本:

>>> import random
>>> random.choice([1,2,3,4,5,6])
3
>>> random.choice([1,2,3,4,5,6])
4

如果您希望在Java中执行此操作,则应该考虑将元素复制到某种随机访问集合(例如ArrayList)中。因为,除非你的集合很小,否则访问所选元素的代价会很高(O(n)而不是O(1))。[ed: list copy也是O(n)]

或者,您可以寻找另一个更符合您需求的Set实现。来自公共集合的ListOrderedSet看起来很有前途。

读完这篇文章后,我能写的最好的是:

static Random random = new Random(System.currentTimeMillis());
public static <T> T randomChoice(T[] choices)
{
    int index = random.nextInt(choices.length);
    return choices[index];
}