К сожалению, это невозможно сделать эффективно (лучше, чем 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, но для меня это слишком много сегодня:)