我正在寻找一种内置的Python数据结构,可以add
一个新元素,remove
一个现有元素,并选择一个随机元素,所有这些都优于O(n)时间
我希望set
可以做到这一点,但是AFAIK,从Python集中选择随机元素的唯一方法是random.choice(list(my_set))
,这需要花费O(n)时间。
我非常喜欢Python内置的解决方案,因为我需要高效且易于部署。不幸的是,Python似乎没有内置的树数据类型。
答案 0 :(得分:8)
Python没有内置数据结构,可以满足您的所有3个要求。
也就是说,自己实现一棵树是相当简单的。
另一种选择是将字典与列表结合起来创建实际上是一个集合,同时维护其项目列表:
import random
class ListDict(object):
def __init__(self):
self.item_to_position = {}
self.items = []
def add_item(self, item):
if item in self.item_to_position:
return
self.items.append(item)
self.item_to_position[item] = len(self.items)-1
def remove_item(self, item):
position = self.item_to_position.pop(item)
last_item = self.items.pop()
if position != len(self.items):
self.items[position] = last_item
self.item_to_position[last_item] = position
def choose_random_item(self):
return random.choice(self.items)
由于列表上的唯一操作是.pop()
和.append()
,因此它们不应超过常量时间(至少在大多数Python实现中)。
答案 1 :(得分:0)
一棵树似乎过大了。除非我缺少某些内容,否则您可以对O(1)中的所有操作使用dict。这只是概念证明;您可以将dict或set设置为子类,并执行更完整的实现。
import random
from time import perf_counter
class SetFastRnd:
def __init__(self, it):
self.elems = {k: k for k in it}
def __contains__(self, elem):
return elem in self.elems
def __iter__(self):
return iter(self.elems)
def __len__(self):
return len(self.elems)
def __getitem__(self, elem):
return self.elems[elem]
def __repr__(self):
return str(self.elems.keys())
def add(self, elem):
self.elems[elem] = elem
def remove(self, elem):
del self.elems[elem]
if __name__ == "__main__":
n = 100000000
s = SetFastRnd(range(n))
start_time = perf_counter()
print(n // 2 in s) # True
s.remove(42)
print(42 in s) # False
s.add(42)
print(42 in s) # True
random.choice(s) # O(1)
random.choices(s, k=5) # O(1)
print("with dict:", perf_counter() - start_time)
s = set(range(n))
start_time = perf_counter()
random.sample(s, 1) # O(n)
print("with set: ", perf_counter() - start_time)
输出:
True
False
True
with dict: 0.0043047999999998865
with set: 0.5654710000000005