我试图对与某个给定集合的子集相关联的值进行常量查找,但不保证顺序。
我将积极使用原始集合,删除/添加元素,并希望在我继续查找剩余元素的关联值。
例如,如果我的给定集合为given = {1, 2, 3}
,也许我会构建一个看起来像这样的字典...
{
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',
}
假设我通过given.remove(2)
从给定集合中删除了一个元素,留下了{1, 3}
,我希望看到相关的值。我必须将我的设置强制为冻结,以便在dict中查找并检索值'panda'
。因此,如果我通过given.add(2)
添加元素,还原原始{1, 2, 3}
,我再次必须强制转换为冻结,然后才能从字典中检索banana
。
我觉得必须胁迫冻结是O(n)操作,这违背了O(1)查找的目的。
有没有办法在Python中更有效地实现这种查找?或者有没有可以帮助我的数据结构?
我在Py2.7上,但如果Py3对此更好,请告诉我。谢谢!
答案 0 :(得分:1)
我觉得必须胁迫冻结是O(n)操作,这违背了O(1)查找的目的。
它的大小为given
,而不是dict的大小。为了进行比较,采用散列在given
的大小上也是线性的,所以即使你不必构造一个冻结集,你仍然具有相同的渐近复杂度。
如果这个成本对你来说太贵了,你可以尝试编写自己的set wrapper类,其中包含允许增量更新的哈希函数,并打破通常的条件,即哈希对象不会以影响哈希值的方式变化。我个人在基于Zobrist hashing的方案中获得了良好的结果,其中集合的元素被分配了随机生成的哈希码,该哈希码在程序的生命周期中持续存在,并且该集合的哈希是异或所有元素哈希。添加或删除元素时,可以通过使用元素的散列对其进行异或来更新集合的散列。
答案 1 :(得分:0)
基于user2357112的回答。未经测试,因为我失去了兴趣。
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
答案 2 :(得分:0)
如何用元素列表中的二进制列表中的单词的指示编码:
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)。
根据https://wiki.python.org/moin/TimeComplexity,删除系列中的元素会花费一些。也许最好直接调用get_indices。