我有一个50K的字符串(城市名称)列表,我需要一个最小的字符三元组(prefarably n-grams),其中每个字符串至少被一个trigram命中一次。考虑以下列表: [“阿姆斯特丹”,“鹿特丹”,“哈勒姆”,“乌得勒支”,“格罗宁根”]
识别三元组的列表有4个,应该是(可能的替代方案):
['ter', 'haa', 'utr', 'gro']
我以为我的解决方案能找到正确的答案,但在其他列表中使用时却给出了错误的答案。在
^{pr2}$到目前为止,我得到了两个答案,但都有缺陷。来自Rupesh的一个适合于小于10个项目的列表。我的单子上有超过5万个项目。来自mujjiga的人确实提出了一个解决方案,尽管不是完美的。在
一个悬赏Python忍者谁提出了一个完美的解决方案,规模。 如果它表现良好,每次运行时都给出相同的解决方案,那就奖励kuddos!在
以下是@mujjiga答案的理论分析:
您可以创建共享同一ngram的单词类。你需要从这些类中选择最小数量的类(即ngram的最小数量)来覆盖整个单词集。这是set cover problem。不幸的是,这个问题是NP难的(不是NP complete,谢谢@mujjiga)。(编辑:因此,没有已知的解决方案可以在合理的时间内为您提供预期的结果。贪婪算法几乎是最好的解决方案(见https://cs.stackexchange.com/questions/49777/is-greedy-algorithm-the-best-algorithm-for-set-cover-problem)。在
请注意,即使贪婪算法也可能给出奇怪的结果。取集合},两个子集就足够了。在
{a, b}, {b, c}, {c, d}
和超集{a, b, c, d}
。这三个子集是最大的。如果首先使用{b, c}
,则需要另外两个子集来覆盖超集。如果使用{a, b}
或{让我们使用贪婪算法,并考虑实现。创建将ngrams映射到单词的字典的代码非常简单:
如果键},否则创建一个空集。这就是
setdefault
存在,setdefault
等同于{O(|all_words|*|len max word|)
的复杂性。在现在,我们想把单词最多的ngram从字典中删除。重复,直到你得到你想要的单词。在
下面是一个简单的版本:
^{pr2}$输出:
第二步有一个
O(|all_words|*|ngrams|)
的复杂性,因为要循环查找max和字典的更新。因此,总体复杂性是O(|all_words|*|ngrams|)
使用优先级队列可以降低复杂性。检索最佳ngram的代价是
O(1)
,但是更新映射到ngram的单词的len
具有优先权O(lg |ngrams|)
:使用此代码,总体优先级降为
O(|all_words|*|lg ngrams|)
。也就是说,我很想知道这是否比你5万件物品的原始版本快。在上述解决方案失败的原因是
Counter
以无序的方式返回三元组,因此,如果您多次运行解决方案,您也将随机获得所需的解决方案在这里,我将按照包含三元组列表的最小到最高的顺序进行,然后在找到解决方案后立即返回。在
下面是集合覆盖贪婪算法的一个实现。在我的第二本英语词典里,大约有5万个单词在我的机器上运行。输出并不总是最优的,但在实践中往往是接近的。你也许可以用一个外部的整数编程库来解决你的实例的最优化问题,但我不知道你是否想朝这个方向发展。在
下面的代码动态维护ngram和未覆盖文本的二分图。一个微妙的地方是,由于Python的标准库中缺少一个侵入式堆,所以我利用了这样一个事实:键只会增加到伪堆。每一个ngram都在堆中,分数小于或等于它应该的值。当我们拉取最小值时,如果它小于应该的值,我们就用更新后的值把它放回去。否则,我们知道这是真正的最小值。在
此代码应产生确定的输出。在每一个步骤中,它选择词典编纂最小的ngram来覆盖最大数量的未覆盖文本。在
相关问题 更多 >
编程相关推荐