Algorithm 有效地从集合D中包含的集合C中查找所有集合S
我从目标集的集合Algorithm 有效地从集合D中包含的集合C中查找所有集合S,algorithm,scala,set,subset,Algorithm,Scala,Set,Subset,我从目标集的集合C开始。每组包含单词(或不带空格的字符串)。当我重复句子时,我可以把这个句子看作是一组观察到的单词 d>代码>。我的问题是,对于每个句子,我想在C中找到所有S,这样D包含S。换句话说,我想找到C中的所有单词集,它们的所有单词都在句子中 例如,考虑 C>代码>以下内容: {fell, ate} {cat, snail, tiger} {tree, bush, jalepeno} {pen, paperclip, stapler} 现在,如果我看到“树倒在灌木丛上,
C
开始。每组包含单词(或不带空格的字符串)。当我重复句子时,我可以把这个句子看作是一组观察到的单词<代码> d>代码>。我的问题是,对于每个句子,我想在C
中找到所有S
,这样D
包含S
。换句话说,我想找到C
中的所有单词集,它们的所有单词都在句子中
例如,考虑<代码> C>代码>以下内容:
{fell, ate}
{cat, snail, tiger}
{tree, bush, jalepeno}
{pen, paperclip, stapler}
现在,如果我看到“树倒在灌木丛上,因为它吃了一瓶杰莱普诺”,我想退回以下两套
{fell, ate}
{tree, bush, jalepeno}
这似乎有点类似于trie。然而,它只是相似,因为我没有匹配句子中的所有单词,而是匹配任何子集。一个想法是在每个级别用Map[String,PseudoTrie]
以类似trie的数据结构表示集合C
,并在我按排序顺序迭代句子中的单词时遵循多条路径
我知道这类似于搜索查询。但是,如果我需要迭代所有的句子,只要每个句子的计算速度很快就可以了。在C
中迭代每个集合并执行包含操作太慢
更新
我想人们可能会对我的一些成果感兴趣。这些计时是在100000句句子上进行的,而D
中的每个目标集C
大约有4到5个单词
此外,我认为我最初的想法很好(许多层的反向索引),但它相当复杂,我已经有了相当好的加速 我们将从您要搜索的句子语料库开始:
val corpus = Seq(
Set("fell", "ate"),
Set("cat", "snail", "tiger"),
Set("tree", "bush", "jalapeno"),
Set("pen", "paperclip", "stapler")
)
一种相当有效的表示方法是将其表示为一个位表,词汇类型作为列,句子作为行。我们定义了两个用于转换为该表示形式的函数:
import scala.collection.immutable.BitSet
def vocabulary(c: Seq[Set[String]]) = c.reduce(_ union _).zipWithIndex.toMap
def convert(s: Set[String], v: Map[String, Int]) = (BitSet.empty /: s) {
(b, w) => v.get(w).map(b + _).getOrElse(b)
}
以及一个功能,用于在语料库c
中搜索给定句子s
包含的所有句子:
def search(s: BitSet, c: Seq[BitSet]) = c.filter(x => (x & s) == x)
这将非常快,因为它只是一个按位“and”和语料库中每个句子的相等比较。我们可以测试:
val vocab = vocabulary(corpus)
val table = corpus.map(convert(_, vocab))
val sentence = convert(
"The tree fell over on the bush because it ate a jalapeno".split(" ").toSet,
vocab
)
val result = search(sentence, table)
这给我们提供了列表(位集(2,6),位集(5,7,10))
。为了确认这是我们想要的:
val bacov = vocab.map(_.swap).toMap
result.map(_.map(bacov(_)))
这是所需的
列表(Set(fall,ate),Set(jalapeno,tree,bush))
。您通常可以通过创建一个字典,为每个单词指定一个数字,然后从字符串之间的比较切换到数字之间的比较来加速基础比较
一个简单的方法是从每个集合中随机选取一个单词,然后创建一个字典,将每个单词映射到一个集合列表,从中选择单词。然后,给定一个句子,在字典中查找其中的每个单词,看看句子中是否包含任何集合列表
您可能能够快速检测到集合何时不是句子的子集。为每个字创建一个稀疏的64位模式,并将每个集合表示为其中每个字的位模式的or。将一个句子表示为其所有单词的或。那么如果设置&~语句!=0,则该集合不包含在句子中。如果某个集合未通过此测试,则该集合不是子集。不幸的是,如果它通过了测试,它可能仍然不是一个子集,您将不得不使用较慢的测试来确认这一点,但是如果在第一个关卡上有足够多的集合失败,您可以节省时间。根据经验,我会让每个64位模式都有k个随机选择的位集,选择k使得代表句子的64位模式有大约一半的位集-但是你可能可以在信封背面稍微考虑一下,找到一个更好的目标。如果你只是在一个句子中找到一个特定的单词后才进行这个测试,你当然可以在你创建的集合中不包含这个单词,把它的出现视为理所当然
假设一个单词独立设置64位位图中的每一位,但未能以概率x设置它。然后,在一个包含n个单词的句子中,位图中没有为该句子设置一个位,概率为x^n。对于一个有k个单词的集合,如果它是由句子而不是由单词设置的,我们可以根据该位进行丢弃,概率为(1-x^k)x^n。如果我对此进行微分,我会在x=(n/(n+k))^(1/k)处得到一个最大值。如果我设置n=20k=4,那么我想要x=0.9554,一个句子中的位大约40%的时间是清晰的,一个位大约7%的时间是丢弃的。但是我们有64位,它们在这个模型上几乎是独立的,所以我们在98%的时间里丢弃了完全的不匹配。An对于这类问题非常有用。作为一个起点,考虑创建一个从“代码> C <代码>中的单词映射到包含该单词的所有集合的列表,从而具有类型<代码> MAP[String,List[St[Str] ] ] < /代码>;这是反向索引
使用反向索引,您可以找到D
中包含的集合,而无需检查