Malheureusement, cela ne peut pas être fait efficacement (mieux que O (n)) dans l'un des conteneurs d'ensemble de la bibliothèque standard.
C'est étrange, car il est très facile d'ajouter une fonction de sélection aléatoire aux ensembles de hachage ainsi qu'aux ensembles binaires. Dans un ensemble de hachage pas trop clairsemé, vous pouvez essayer des entrées aléatoires, jusqu'à ce que vous obteniez un résultat. Pour un arbre binaire, vous pouvez choisir aléatoirement entre le sous-arbre gauche ou droit, avec un maximum de O (log2) étapes. J'ai implémenté une démo de ce qui suit ci-dessous:
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
J'ai eu [995, 975, 971, 995, 1057, 1004, 966, 1052, 984, 1001] comme sortie, donc la distribution semble bonne.
J'ai lutté avec le même problème pour moi-même, et je n'ai pas encore décidé que le gain de performances de ce choix plus efficace vaut la peine d'utiliser une collection basée sur python. Je pourrais bien sûr l'affiner et le traduire en C, mais c'est trop de travail pour moi aujourd'hui :)