动态子集的Python高效查找结构?
我试图对与某个给定集合的子集相关联的值进行常量时间查找,其中顺序是不保证的 我将积极处理原始集合,删除/添加回元素,并希望在继续操作时查找其余元素的关联值 例如,如果我的给定集是动态子集的Python高效查找结构?,python,dictionary,set,immutability,Python,Dictionary,Set,Immutability,我试图对与某个给定集合的子集相关联的值进行常量时间查找,其中顺序是不保证的 我将积极处理原始集合,删除/添加回元素,并希望在继续操作时查找其余元素的关联值 例如,如果我的给定集是gived={1,2,3},那么我可能会构建一个如下所示的dict { frozenset([]): 'apple', frozenset([1]): 'orange', frozenset([2]): 'ice bear', frozenset([3]): 'peach', fr
gived={1,2,3}
,那么我可能会构建一个如下所示的dict
{
frozenset([]): 'apple',
frozenset([1]): 'orange',
frozenset([2]): 'ice bear',
frozenset([3]): 'peach',
frozenset([1, 2]): 'grizzly',
frozenset([2, 3]): 'pear',
frozenset([1, 3]): 'panda',
frozenset([1, 2, 3]): 'banana',
}
假设我通过gived.remove(2)
从给定集合中删除一个元素,留下{1,3}
,我想查看关联的值。为了在dict中查找并检索值“panda”
,我必须将我的集合强制为frozenset。因此,如果我通过given.add(2)
添加回元素,恢复原始的{1,2,3}
,那么在从dict中检索banana
之前,我必须再次强制使用frozenset
我觉得强制使用冻结集是一个O(n)操作,它违背了O(1)查找的目的
有没有一种方法可以更有效地在Python中实现这种查找?或者这里有什么数据结构可以帮助我吗
我使用的是Py2.7,但如果Py3在这方面更好,请告诉我。谢谢
我觉得强制使用冻结集是一个O(n)操作,它违背了O(1)查找的目的
它在给定的的大小上是线性的,而不是在dict的大小上。为了进行比较,hash在给定的的大小上也是线性的,因此即使不必构造冻结集,也会有相同的渐近复杂性
如果此成本对您来说太高,您可以尝试使用允许增量更新的哈希函数编写自己的集合包装类,并打破可哈希对象不能以影响其哈希值的方式改变的通常条件。我个人在基于的方案中取得了很好的效果,其中集合的元素被分配了随机生成的哈希代码,该代码在程序的生命周期内一直存在,集合的哈希是所有元素哈希的XOR。添加或删除元素时,可以通过将其与元素的哈希进行异或来更新集合的哈希。基于用户2357112的答案。没有经过测试,因为我失去了兴趣
from random import Random
class FastRehashableSet(set):
_initial_hash = 12345
def __init__(self, seq=()):
super(FastRehashableSet, self).__init__(seq)
self._hash = self._initial_hash
for x in seq:
self._hash_single_value(x)
def _hash_single_value(self, val):
# Introduce extra randomness since the intended elements are ints
# which just return themselves when hashed
self._hash ^= Random(hash(val)).randrange(4294967296)
def __hash__(self):
return self._hash
def add(self, elem):
super(FastRehashableSet, self).add(elem)
self._hash_single_value(elem)
def remove(self, elem):
super(FastRehashableSet, self).remove(elem)
self._hash_single_value(elem)
def discard(self, elem):
change = elem in self
super(FastRehashableSet, self).discard(elem)
if change:
self._hash_single_value(elem)
def pop(self):
val = super(FastRehashableSet, self).pop()
self._hash_single_value(val)
return val
def clear(self):
super(FastRehashableSet, self).clear()
self._hash = self._initial_hash
# You get the idea, I'm not doing these
def update(self):
raise NotImplemented
def intersection_update(self):
raise NotImplemented
def difference_update(self):
raise NotImplemented
def symmetric_difference_update(self):
raise NotImplemented
如何从元素列表中以二进制形式对列表中的单词标记进行编码:
words = ["apple","orange","ice bear","peach","grizzly","panda","pear","banana"]
def get_indice(L):
return sum(2**(i-1) for i in L)
# initial serie of elements
serie = [1,2,3]
# first computation of indice
ind = get_indice([1,2,3])
print serie,words[ind]
# remove the 2
val = 2
serie.remove(val)
ind -= 2**(val-1)
print serie,words[ind]
# add the 2
val = 2
serie.append(val)
serie = sorted(serie)
ind += 2**(val-1)
print serie,words[ind]
输出:
[1, 2, 3] banana
[1, 3] panda
[1, 2, 3] banana
请注意,第一次计算需要N次运算,其中N是级数中的元素数,这比单词中的元素数要好。以下添加和删除操作是直接的,成本为O(1)
因为去除意甲中的元素可能会花费一些费用。也许直接调用get_索引更好。这是O(n)
其中n
是键集的大小,而不是字典的大小,这是一个重要的区别。这些电视机有多大?无论如何,在字典中查找仍然需要对键进行哈希运算,该键也必须是O(n)
。谢谢Alex,这是绝对正确的。我想得不对。集合相当大,但理论上哈希本身也必须遍历键的长度。啊,是的。与键的大小成线性关系,而不是与单词的大小成线性关系。谢谢你,这很有意义。