我获得了N个C布尔数组。我想将这些组织成一个数据结构,允许我尽可能快地执行以下操作:给定一个新数组,如果此数组是任何存储数组的“超集”,则返回true。对于超集,我的意思是:如果A [i]对于B [i]为真的每个i都为真,则A是B的超集。如果B [i]为假,那么A [i]可以是任何东西。
或者,就集合而不是数组而言:
将N个集合(每个都有C个可能的元素)存储到数据结构中,这样您就可以快速查找给定集合是否是任何存储集合的超集。
构建数据结构可能需要尽可能长的时间,但查找应该尽可能高效,并且数据结构不能占用太多空间。
我认为这本身就是一个有趣的问题,但对于我真正想要解决的问题,您可以假设以下内容:
对于O(NC)查找:只需迭代所有数组。但这太慢了。
对于O(C)查询:我在这里有一个很长的描述,但正如Amit在评论中指出的那样,它基本上是BDD。虽然这具有很高的查找速度,但它具有指数数量的节点。 N和C如此之大,这需要太多空间。
我希望在这个O(N * C)和O(C)解决方案之间,可能有一个不需要指数空间的O(log(N)* C)解决方案。
对于O(sqrt(N)C)查找:将数组存储为prefix trie。查找数组A时,如果A [i] = 0,则转到相应的子树,但如果A [i] = 1,则访问两个子树。
我的直觉告诉我,如果你假设存储的数组是随机的,那么这应该使查找O(sqrt(N)C)的(平均)复杂度成为可能。但是:1。他们不是,阵列稀疏。 2.这只是直觉,我无法证明。
我会尝试这个新想法和BDD方法,看看哪两个最好用。
但与此同时,这个问题不会经常发生吗?它没有名字吗?还没有以前的研究吗?我真的觉得我在这里重新发明轮子。
答案 0 :(得分:5)
只是将一些背景信息添加到前缀trie解决方案,最近我发现了以下文件:
I.Savnik:快速子集和超集查询的索引数据结构。 CD-ARES, IFIP LNCS, 2013.
本文提出了set-trie数据结构(容器),它使用 trie数据结构为高效存储和查询集合提供支持,支持查找所有超集/子集的操作。来自集合集合的给定集合。
对于任何对实际实现感兴趣的 python 用户,我想出了一个python3包,部分基于上面的论文。它包含一个基于trie的集合容器,以及一个映射容器,其中键是集合。您可以在github上找到它。
答案 1 :(得分:3)
我认为前缀trie是一个很好的开始。
由于您的数组稀疏,我还会批量测试它们。如果(B1 ∪ B2) ⊂ A
,则包括两者。因此,我们的想法是成对地对数组进行OR包,并重复直到只有一个“根”数组(它只需要两倍的空间)。它允许在之前的问题中回答“是”,这主要是有用的如果您不需要知道数组实际上是否包含。
独立地,您可以为每个数组申请一个保持排序的哈希函数。
即:B ⊂ A ⇒ h(B) ≺ h(A)
ORing位在一起就是这样一个函数,但您也可以在数组的足够分区中对每个1位进行计数。在这里,您可以更快地消除候选者(对特定阵列回答“否”)。
答案 2 :(得分:2)
您可以通过首先将集合列表减少到“最小”集来简化问题:仅保留那些不是任何其他集的集的集。问题仍然存在,因为如果某些输入集A
是您删除的某些集合B
的超集,那么它也是{至少一个“最小”子集C
的超集。 {1}}未被删除。这样做的好处是你倾向于消除大型集合,这使问题更便宜。
从那里我会使用某种ID3或C4.5算法。
答案 3 :(得分:0)
在trie解决方案和@mmihaltz提到的论文的基础上,还可以通过使用已经存在的高效python trie实现来实现一种找到子集的方法。下面,我使用软件包datrie。唯一的缺点是必须将键转换为字符串,这可以使用"".join(chr(i) for i in myset)
完成。但是,这将元素范围限制在110000左右。
from datrie import BaseTrie, BaseState
def existsSubset(trie, setarr, trieState=None):
if trieState is None:
trieState = BaseState(trie)
trieState2 = BaseState(trie)
trieState.copy_to(trieState2)
for i, elem in enumerate(setarr):
if trieState2.walk(elem):
if trieState2.is_terminal() or existsSubset(trie, setarr[i:], trieState2):
return True
trieState.copy_to(trieState2)
return False
trie
可以像字典一样使用,但是必须在开头提供可能的元素范围:
alphabet = "".join(chr(i) for i in range(100))
trie = BaseTrie(alphabet)
for subset in sets:
trie["".join(chr(i) for i in subset)] = 0 # the assigned value does not matter
请注意,上面的trie实现仅适用于大于(但不等于)0
的键。否则,整数到字符的映射将无法正常工作。这个问题可以通过索引移位来解决。
可以找到{y3}}来实现也涉及元素转换的cython实现。