寻找一组集合的“最佳”组合方式

18

我有一个包含英语句子的字符串集合,名为 sentences。 我想创建一个名为 sentences2 的子集,该子集仅包含具有20个唯一单词的句子。当然,可以存在很多这样的子集,但是我想找到最“好”的那一个,所谓的“好”指的是这个子集中所有单词在 sentences2 中的表示都尽可能高。

下面的示例将进一步说明我所说的“最佳”:

如果我要过滤出以下单词集合的 sentences

(i,you,do,think,yes,dont,can,it,good,cant,but,am,why,where,now,no,know,here,feel,are)

我将得到以下内容:

sentences2 = set(("where are you now", "here i am", "can you do it", "yes i can", "but can i do it", "no you cant", "do you feel good", "yes i do", "why are you here", "i dont know", "i think i know why", "you dont think", "yes i do", "no you dont", "i dont think you think", "i feel good", "but i am good", "i cant do it now", "yes you can", "but i cant", "where do you think i am"))

并且在这里,我们可以看到每个单词至少出现了两次,如果我们在sentences2上使用计数器就能看到:

c = collections.Counter({'i': 13, 'you': 10, 'do': 6, 'think': 5, 'dont': 4, 'can': 4, 'good': 3, 'but': 3, 'am': 3, 'it': 3, 'cant': 3, 'yes': 3, 'know': 2, 'no': 2, 'here': 2, 'why': 2, 'feel': 2, 'are': 2, 'now': 2, 'where': 2})

如果每个单词至少出现两次,我们可以说这组20个单词的得分为2。

score = min(c.values())

然而,以下集合:

(i,you,he,do,think,yes,dont,can,it,good,cant,but,am,why,where,now,no,here,she,are)

得分为5,因为如果我使用它来过滤“sentences”,我会得到“sentences2”,其中每个单词至少出现了5次。

所以我需要获取所有可能的20个单词组合的最高分。

以下是我解决此问题的尝试:

sentences = ... # all the sentences in my text
common_words = ... # the hundred most common words in the text
result_size = 20

highest_score = 0
for sample in itertools.combinations(common_words, result_size):

    sentences2 = list(filter(lambda s: set(s).issubset(sample), sentences))
    c = Counter([j for i in sentences2 for j in i])

    if len(c.values()) and min(c.values()) > highest_score:
        # this is the set with the highest score to date
        print(c)
        highest_score = min(c.values())

然而,如果我没有弄错的话,使用这个算法将需要永远的时间来计算出5.3598337040381E+20种组合。你能建议一种更快的算法吗?

请注意,最终的集合中可能包含不到20个单词,这完全没问题。例如,在我的算法中,c.values() 不必匹配 result_size 的大小。

此外,请注意,我期望结果集中的单词在前100个常用单词(common_words 包含100个值)中找到。这也是有意为之。


3
你可以通过在问题中添加一个最小、完整和可验证的示例来使其更加清晰。 - Mazdak
1
所以,您想要找到20个单词,这些单词在任何句子中的最小出现次数都是最高的?可能最好创建一个字典,将单词映射到它们在任何句子中的最小出现次数。 - tobias_k
2
@Baz:一个单词可以在一句话中出现多次吗?这算作什么?如果你要为你的集合选择一个单词,你不会选择总出现次数最多的单词,而是选择出现在最多句子中的单词,我理解得对吗?一句话必须满足什么条件才能符合集合sentence2的要求?比例子更好的方法是给出一个精确的规范...我发现你的第一段很难理解。你能进一步澄清吗? - dingalapadum
1
@dingalapadum 一个单词可能在句子中出现多次,但是我只想让它在这种情况下计算一次。然而这并不是非常重要,目前我的算法也没有正确处理这个问题。我又更新了我的问题,希望现在能更清楚明白。 - Baz
3
你愿意设置500点赏金,但却不上传你真正关心的输入? - David Eisenstat
显示剩余20条评论
6个回答

7
免责声明:您没有指定数据特征,因此我的答案将假设它不是太大(超过1,000,000个句子,每个句子最多1,000个字符)。此外,描述有些复杂,我可能没有完全理解问题。 解决方案:
与其专注于不同的组合,为什么不为您使用最常用的100个单词创建一个哈希表(在Python中称为dict),然后遍历句子数组,并针对每个句子中的每个单词增加其相应值(如果它已经在字典中)。
最后,只需根据每个单词(键)的出现次数(值)对此哈希表进行排序,然后使用最常见的20个。
复杂度:
快速查看算法,得到:
遍历N个句子,每个句子都包含M个单词,增加哈希表值。最后对(单词,出现次数)对的数组进行排序。这是可以忽略的(哈希表大小是恒定的,即100个常用单词),并提取前20个。
时间复杂度:O(N*M)
空间复杂度:O(1)(我们不需要存储句子,我们只有哈希表)

示例代码:
这是一个快速的伪代码:

word_occur_dict = {#initialized with frequent words as keys, and zero as value for all}
for sentence in sentences:    #for each sentence
    sentence_words = sentence.split(" ")    #construct the word list
    for word in sentence_words:        #for each word
        if word in word_occur_dict:         #if it is a frequent word, increase value
            word_occur_dict[word]++
final_result = sort_dict(word_occur_dict)[:20] #returns list of tuples

Python 代码:

import operator

common_words = ["do","think","yes","dont","can","it","good","cant","but","am","why","where","now","no","know","here","feel","are","i","you","he","she"]
common_words_dict = {}
sentences = ["where are you now", "here i am", "can you do it", "yes i can", "but can i do it", "no you cant", "do you feel good", "yes i do", "why are you here", "i dont know", "i think i know why", "you dont think", "yes i do", "no you dont", "i dont think you think", "i feel good", "but i am good", "i cant do it now", "yes you can", "but i cant", "where do you think i am"]

for w in common_words:    #initialize the dict
    common_words_dict[w] = 0    

for sentence in sentences:    #for each sentence
    sentence_words = sentence.split(" ")    #construct the word list
    for word in sentence_words:        #for each word
        if word in common_words_dict:         #if it is a frequent word, increase value
            common_words_dict[word] = common_words_dict[word]+1

sorted_word_dict = sorted(common_words_dict.items(), key=operator.itemgetter(1))

print sorted_word_dict[::-1][:20]

顺便提一下,“他”和“她”在句子中没有出现,但是你说以下单词组合的得分为5:
(i,you,he,do,think,yes,dont,can,it,good,cant,but,am,why,where,now,no,here,she,are)
我是否误解了问题。
值得赞扬的地方在于:StackOverflow:按值对Python字典进行排序

这难道不只是最初(而且相当简单)的部分吗?也就是确定所有输入中出现最频繁的单词?在我看来,从排序后的列表中选择前20个并不一定是所述问题的解决方案。 - Bert te Velde

5

步骤1应该是创建一个数据结构,其中只包含在common_words中出现的sentences中的单词。该结构还可以有单词出现的次数和一组整数,用于引用单词所在的句子。

counts[..., {  word:string,  count:number,  ids:Set<number>}, ...]

一些伪代码

countsMap = Map()
For i = 0 To sentences.Size - 1
  sentence = sentences[i]
  For Each word in sentence
    If Not countsMap.Contains(word) Then
      countsMap.Add(word, {word:word, count:0, ids:Set()})
    End If
    value = wordMap.Get(word)
    If Not value.ids.Contains(i) Then
      value.Count++
      value.ids.Add(i)
      countsMap[word] = value
    End If
  Next
Next
counts = countsMap.Values

Idealistic STEP 2 如果你很幸运,你的counts数据类型包含小于40个条目,你可以在合理的时间内使用单台计算机对C(n, 20)组合进行详尽搜索,其中C(38, 20) ~= 330亿。这将涉及迭代组合并相交ids集合,最终集合大小是您的最小分数。

一些伪代码

bestScore = 0
bestCombo = null
For Each combo in Combinations(counts, 20)
  score = combo.Reduce((prev, curr) => prev.ids.Intersect(curr.ids)).Size
  If bestScore < score Then
    bestScore = score
    bestCombo = combo
  End If
Next

现实步骤2 在大多数情况下,您的计数将包含超过40个独特单词,因此您必须接受最佳猜测/近似值。我可能会采取以下措施:使用上述代码,但不是选择20而是选择2,在得分方面按降序排序并取10个。

一些伪代码

list = []
For Each combo in Combinations(counts, 2)
  score = combo[0].ids.Intersect(combo[1].ids).Size
  list.Add( { score:score, words:[ combo[0].word, combo[1].word ] } )
Next
// sort descending by score
list.Sort((a, b) => b.score - a.score)
// grab the 20 best words
result = Set()
i = 0
While result.Size < 20
  result.Add(list[i].words[0])
  result.Add(list[i].words[1])
  i = i + 1
End While

你会得到一个大于1的最终分数吗?从统计学上讲,这取决于有多少个独特的单词和句子,但可能不会。

编辑 一个实现说明和更正。交集操作可以得到单词所在句子编号的集合的交集,但是需要减去1(从0开始计数),才能得到最小分数。例如,“Dog”在句子1和2中;“Cat”在句子2和3中;“Frog”在句子4中;[1,2] /\ [2,3] /\ [4]的交集为[],但是最小分数为1,即result.Size() + 1。同样地,只有“Dog”和“Cat”[1,2] /\ [2,3] = [2]的集合大小为1,但是最小分数为2。


2
@BurhanKhalid - 你说得对,这不是任何特定的编程语言,而是伪代码 - 一种类似简化编程语言的符号表示法,用于程序设计 - 它是算法中步骤的更高抽象级别。 - Louis Ricci
@感谢Louis Ricci。您能解释一下这行代码的含义吗:score = combo.Reduce((prev, curr) => prev.ids.Intersect(curr.ids)).Size。combo的类型是什么,prev和curr又是什么? - Baz
@Baz - combo 是我上面定义的 count 类型元素的集合。Reduce 是一个常见的 map/reduce 术语,用于将集合缩减为单个值。因此,所有行所做的就是循环遍历 combo 的所有元素(这些元素包含一个单词、计数和 ids 属性),并将所有 ids 集合相交,这将返回包含所有单词的 sentences 中唯一索引的集合,其大小即为您的分数。 - Louis Ricci

4
我建议您尝试以下方法。我没有数学证明它会产生绝对最佳的“分数”,或者其他“好度量标准”,但我相信它可能会帮助您,除非当然,情况需要您真正证明并找到最佳结果。
我不会说Python,但策略和随后的实现足够简单,甚至不需要伪代码来解释。虽然我会用很多话,但不是因为它非常复杂,而是为了清晰(我希望如此)。
您需要一种对矩阵进行对角化的方法,或者至少找到与最大特征值对应的特征向量。并非每种语言都本地提供这种方法。在C ++中,您可以使用Eigen库。在我用于测试的C#中,我连接了MathNet。在Python中,我不知道。对于对角化/特征向量设施的要求有限:矩阵始终是实数、对称的,所有元素都是正数。但是,尽管对角线元素至少与其行/列中的任何其他元素一样大,但该矩阵肯定不是“对角线优势”的。
您提出的问题可以抽象地表示为整数,而不是单词,这使得它更方便(对我而言...)来制定和实现(但否则,它是相当无关紧要的)。实际上,我们只需要您的“common_words”,因此100个整数[0..99],您只需将单词放入数组或列表中并使用其索引即可一次性设置单词和整数之间的映射。
顺便说一句:该策略可能比完全通用的整数问题更适合您的语言应用程序,因为我们基本上利用了成对相关性(如果有的话)在项(同一句子中的单词)之间,而三元组、四元组等的主导强相关性可能会使其效率降低(但我们将为三元组做一些事情)。显然,在格式良好的语言中,您期望存在相关性:“am”经常与“I”一起出现,而尽管“have”的总量可能比“has”多,但一旦您找到“he”,您可能更有可能在同一句子中获得“has”而不是“have”。等等。
需要一些定义。
NCommon是您的common_words中的元素数量(i.c. 100)。common_words是整数[0..NCommon-1]。
NMaxSet是您的问题限制(i.c. 20),最终可以使用的“单词”的最大数量。
F是一个过滤器:您使用它来定义哪些句子包含在某个集合中。最后,您必须调整您的过滤器,使F.Count不超过NMaxSet。
M(N)是一个NxN方阵;行和列索引在[0..N-1]中。

S(F)是满足过滤器F的句子集合(来自问题输入)。在此,“句子”始终是在[0..NCommon-1]范围内的整数集合,详见上文。同一句子中的重复单词无关紧要(问题描述),因此这种重复会从我们的整数句子中移除。

开始准备。

初始化一个矩阵MCommon = M(NCommon)。 创建包含所有common_words的FCommon过滤器。

过滤输入,删除句子中的重复单词。 这将产生一个句子集合S0 = S(FCommon),这是您的“真实”输入:所有不相关的内容都已被删除。

在接受句子到S0时,即时填充矩阵MCommon: {for (j in sentence): for(k in sentence): M(j,k)+=1}。 该矩阵是对称的,因此您可以只填写右上方三角形,然后在结束时将其反映到左下角三角形中。

扫描完成后,M[j,k]是包含j的"句子"中k出现次数的数量(反之亦然:矩阵是对称的)。 M[k,k]是S中所有句子中k的总计数。 M是(成对)相关矩阵,告诉您{j,k}组合在底层句子集S中出现的可能性有多大。

(始终如此,在处理这样的矩阵时:如果最终存在空列(因此也是行): 同时删除支持矩阵的相应条目,因为显然它不起作用。 从现在开始,我们将假定已经完成了这个步骤(除非另有说明),即矩阵中没有列/行为空。)

计算结果(主要方法,我们将在下面进行精化):

计算与最高特征值对应的MCommon的特征向量E: E是一个具有NCommon系数的数组(向量)。

设置NTarget=NSetMax

确定E中NTarget最大系数的索引,我们不关心它们的值,而是它们的索引。 将这些索引放在一起:它们定义了一个新的过滤器F(NTarget)。

通过新的过滤器运行S0,以生成STarget。 计算所有“单词”出现次数,找到它们的最小值: 这就是您的“集合质量”值。 例如,您可以通过计算相关的MTarget并扫描对角线值MTarget[k,k]来执行此操作。 这似乎涉及不必要的额外工作,因为您还计算了离对角线的元素,但我们将看到MTarget可以在后续精化中派上用场。

精化。

A) 我们需要验证删除过滤器F(NsetMax)中一个或多个项目,将其缩小到一些NTarget小于NSetMax的值,是否会获得更好的分数价值。 这需要一些小心:很可能删除两个(或3个,…)项目将改善分数,但删除它们中的任何一个都会恶化分数。

让我们首先(并且是相当合理的)尝试解决这个问题。

在生成STarget的同时,您还会像之前使用MCommon一样填充一个新矩阵MTarget(看到了吗,它很方便...)。大小为NTarget。 获取最大特征值的特征向量。确定最小系数。从过滤器中删除对应的索引。

再次过滤(当然,现在可以使用STarget集合作为输入),并计算分数。 如果更好:标记/记住。 无论如何(是否改进),都要以同样的方式继续,一次减少一个过滤器,直到没有任何东西。

B)人们可能预期,如简要解释的原因,在将过滤器进一步降低到NSetMax以下 - 一次一个 - 可能也在某种程度上适用于第一步,即我们通过一次大规模打击将F(NCommon)降低到F(NTarget = NMaxSet)。

为了适应这一点,我们可能希望从NCommon向下到NMaxSet进行步骤。为了不产生太多的计算开销,我们不会 采取大小为1的步骤,而是每次减少约10%或类似数量。

因此,在上面的II中,不要立即将NTarget设置为NSetMax,而是(例如)设置NTarget = 90。 构造相应的过滤器,将其应用于S0,给出S1,还要生成矩阵M1,获取其特征向量(最大特征值)。 重复:将NTarget设置为80、70、60等。在后期阶段,您可能会变得更加谨慎,将40降低到35、30、28、26...... 在每个步骤中,您都建立并使用以前的结果,以最优化地利用大小和计算工作量的减少。

一直要监视是否始终使用相同的索引作为最大NSetMax(该算法部分的最终值)系数。 这将提供关于最终结果是否可靠的一些信息:预期的最终过滤器对算法路径的稳定性或不稳定性。

C)考虑情况,我们已将其减少到NSetMax,并调查是否应该进一步以一个接一个的方法进行减少。 (如果在接近NSetMax时您采用“一次一个”的方式,那么在(B)的最后阶段也可能适用。)

假设在算法的此阶段,在您的(第一个或稍后的)STarget集合中存在某些“单词”对, 删除这种特定对将改善事情, 而它们各自的特征向量中的任何一个系数都不是最小的。 我不确定这是否可能或甚至可能,但让我们看看如何处理它。 如果(您的)测试表明这是无关紧要的,则可以在最终实现中从算法中删除此功能。

假设我们有一些NTarget,以及相关的过滤器FTarget和矩阵MTarget。(过滤器中的项目('words')顺序始终等于矩阵中的行和列的顺序。)
从MTarget中,我们可以直接推断出如果我们从过滤器中删除第j个项目会发生什么。 当然,矩阵中的第j行和第j列变为空(全部为零)。 更有趣的是,M[j,k] 表示在包含项目j的所有句子中,项目k出现的次数。 因此,当消除所有j(通过将其从过滤器中删除)时,我们预先知道在结果新矩阵MReduced中,元素MReduced[k,k]将恰好减少该M[j,k]值。 (顺便说一下,这提供了一种确定要删除哪个项目的替代方法(如果您选择仅删除一个):最小{j:M[j,j]}是关联集S的分数,而从M中我们可以计算出在删除特定项目j后所有对角线元素将如何改变,从而预先计算出所得分数)
然而,在这里,我们想知道如果删除某对项目j和k,分数将受到影响。 现在,我们需要确定不是j或k的所有p的M[p,p]如何受到影响。 这不能直接从M中计算出来: 删除j会影响行k,虽然我们知道它将如何改变[k.k]和任何其他[p,p],但我们不知道它将如何改变[k,p],这是计算同时删除k也将如何改变[p,p]所需的。 顺便说一下,最终结果对于先删除j再删除k或反之亦然或同时删除两者应该没有影响。
为此,我们需要某种“导数”。 幸运的是,我们可以在不太费计算的情况下计算和处理它(假设NTarget已经相当小,大约20个左右)。
考虑与当前过滤器F相关的“reductionfilter”G(F;j),简单地定义为处理F但忽略其中的项目j。 使用G,我们按照通常的方式计算出“reductionmatrix”N(G),为方便起见,在讨论(和实现)中保持相同的大小(不删除空列/行)。 当然,在这样的N矩阵中,第j行和第j列为空。 其对角线元素将具有上述值(我们可以直接从M中计算出这些值)。 但是,现在我们还有所有由于删除j而导致的非对角线元素。
现在,我们可以从N(G{F;j})中确定如果我们同时删除项目k会发生什么,关于如何从当前矩阵获得预期分数,请参见上面的阐述。 因此,这允许计算删除一对{j,k}时的分数。
如果集合大小为20,则需要计算20个N(G(F;j))矩阵,这相对较小,我认为(现在你的句子集合也比最初变得更小了)。然后,对于20*19/2个唯一的对,计算其结果对-移除分数,并确定从筛选器中删除哪个对(而不是单个元素)。您可以动态比较它与“逐个减少”的减少方法,并根据具体情况做出选择以系统地减少过滤器以获得更好的得分。有很多方法来进行此比较。一个相对简单的方法是:先计算从一对开始,然后再计算单个的减少(总共3个元素)。将其与先删除单个,然后再删除一对进行比较。选择其中较好的,但仅执行第一步(单个或一对),并重复该过程。
请注意,使用这些“派生”的筛选器G、矩阵N和随后的预计算分数,您实际上引入了项目三元组之间的相关性:您确定在同时删除j和k时,所有{j,k,p}对于p的频率会发生什么。当然,这个想法可以扩展到包括四元组及以上,但(a)我不认为它实际上会帮助很多,(b)你走得越远,计算工作量就会增加得越快。我甚至怀疑在此处解释的“三元组”是否相关,但我不是语言专家,除了一些额外的编程工作外,没有什么主要缺点。
D)该策略的核心是依赖具有最大特征值的特征向量来指向随后过滤的相关项。当然,可能会出现两个或多个特征值“几乎”相同,并且相应的特征向量在分析它们的最大分量时可能指向完全不同的项目集。在这种情况下,值得“分支”,即选择其中一个,进行完所有工作,然后重新对其竞争者进行所有操作,并查看它们是否产生更好的结果。如果在解决问题的过程中遇到很多分支,就会出现问题。虽然这不太可能发生,但实现应该有一些策略来以实用的方式处理它。我建议您首先排除任何分支,但是监视“竞争”最大特征值的出现,以便向用户输出警告。或者,您可以实现这样的分支管理,但在程序认为“几乎相等”的情况下非常严格,或者在总共要研究的分支数量上设置某些限制,从而减少计算时间过长的机会。
由于时间不足,我只能在这里结束,希望我已经充分解释了想法以及需要考虑的一些细节和改进。
我还没有花时间为自己整理相关的“真实语言”句子作为输入,并测试那些句子。我在C#中编写了基本步骤,针对随机整数“句子”进行了测试,同时有一些偏见来强制某些“单词”比其他单词更频繁出现,但没有关注“句子”内“单词”之间的相关性。对我来说,结果似乎相当合理,但我没有时间对其进行深入分析。
考虑到我所使用的“随机”序列缺乏结构,而真正的语言预计会展示重要的成对相关性,该策略利用了这一点。我有很大的希望,您能看一下它可能是一个有用的东西。
[编辑]添加说明:在上面,我偶尔不小心谈到“j”,“k”等内容,我很抱歉。有时,“j”指的是某物的第j个条目(矩阵行,过滤器列表中的索引),有时它指的是(通常)过滤器中的相应值。例如,过滤器可能包含18个项目,编号(索引)为0..17,但它们的值始终与原始Common_words列表相关,因此它们可以是{3, 15, 29, 30, 31, 40,...}。然后,当它说“从过滤器中删除j”时,通常会意味着“从过滤器中删除第j个条目(该条目可能具有[0..NCommon-1]中的任何值)”。将过滤器应用于句子时,将过滤器中的值与句子中的值进行比较。我希望上下文 - 结合对推理线路的公平理解 - 总是清楚地表明真正的含义。
[编辑:一些测试结果] 我运行了我的C#实现,使用上述算法(或多或少地:大多数但不是所有的细化/细节被描述)以获得一些印象,看看它会产生什么。
作为输入,我选取了古登堡计划中的4本书(纯文本)。总共有27k个句子,380k个单词(20k个不同的单词),因此是一个相当小的样本。
从这个输入中确定的常用词列表以“the”,“of”,“and”,“to”…(按出现频率排序)开始。
该算法过滤了14个单词(“i”,“what”,“do”,“you”,“it”,“yes”等),以产生8个“集合质量值”的“最佳”结果(只有这些14个单词的139个句子被找到)。
由于我对只使用100个“常用单词”的假设感到怀疑,我预先允许了500个常用单词,而不是100个,果然在成功过滤的单词中,有4个频率编号超过100个(“yes”,“know”,“say”,“think”):“yes”例如,在所有输入中按出现顺序排序,如果您要根据“常用单词”进行排序,则可能是#224。

3
我不确定是否有可能在指数时间内找到最佳解决方案,因为该问题可能没有足够的结构。但是,这里有一个启发式方法来找到“好”的解决方案。
我认为做法是从大小为0的wordset开始,并以聪明的方式逐个添加单词,最多20个。考虑对于给定的wordset_n,每个单词的分数只能增加或保持不变,当添加一个新单词时。只有当第(n+1)个单词降低了最小值时,wordset_(n+1)才可能比wordset_n得分低。因此,我们限制自己只添加将最小值提高或保持不变的单词(但请参见下面的说明)。
因此,第一步大致如下:
  1. 按在sentences中出现的频率对common_words中的单词进行排序。
  2. 将出现频率最高的单词添加到wordset中,直到得分为非零。
  3. 搜索由仅添加增加或维护先前节点得分的单词构建的可能的wordset树(max N = 20)。 因此,向下遍历此树的路径将是wordset_n,wordset_(n+1),wordset_(n+2),其中每个节点都包含前一个节点加上一个新单词。
  4. 选择得分最高的叶节点。
第二步的近似方法是:a)您可能想尝试多个组合来完成第2步。 100选3是162000,这不是不可能的。 b)此外,对于第3步,您可能希望向前看两步,而不仅仅是一步-即,仅当在单词n+2的所有可能性之后,分数仍低于wordset_n时,才拒绝单词放在wordset中的位置n+1。如果存在反相关性,例如只有一个动词的短句不太可能包含另一个动词,则这可能有所帮助。最后c)如果这种方法仍然计算上限,则可以通过关闭未能使(n+1)th提高得分的分支来进一步限制树的构建。
如果sentences中单词的分布相对独立,或仅展示正相关性,则此方法可能会得到接近最优的结果。

1
我会寻找一种数据聚类的形式,以加快在每个20个单词试验中的搜索速度。
重要的数据是句子中的唯一单词。
如果jaccard距离小,则可以认为两个句子很接近。 jaccard距离是1-(句子中单词交集的大小)/(句子中单词并集的大小)。
由于jaccard距离是度量的(符合三角不等式),因此可以构建m-tree索引,以便更快地查找句子。
基于这个基础,可以进行聚类(最佳结果将彼此靠近)。
最后,通过迭代每个句子,并找到K个最近邻居,您可以找到一组最多20个单词。这将为K提供不同的值(共享最佳20个单词的句子)。
我可能会使用数据库来支持此select union和select intersection,从而允许进行集合测试。

0

通过从团问题减少(假设我们将20替换为一个参数)实现NP难。给定一个图,我们正在寻找一个k-团,在这个图中为每个顶点分配一个唯一的单词,对于每个边缘,形成两个单词的句子,并尝试选择包括每个单词k-1次的k选择2个句子。

需要思考是否存在具有合理参数化复杂度的算法。


谢谢你的回答。你介意为一些简单的句子添加一个例子吗? - Baz
能否详细解释一下你的答案,因为我有些难以理解。假设我正在使用networkx,那么我应该按照以下方式为每个句子添加边缘:for word_pair in itertools.combinations(sentence,2): G.add_edge(word_pair[0],word_pair[1])。 - Baz
再次感谢。您所说的“使每个边对应于两个单词的句子”是什么意思?您是指我应该将每个句子的所有“两个单词组合”添加到图中。例如,句子“我在这里”包括“两个单词的句子”:“我是”,“是在这里”,“我在这里”。因此,例如顶点“是”,将以“我是”和“是在这里”的形式有两条边进入它。我也不明白为什么这里与团伙有关。例如,我的解决方案集可能包括单词“他”和“她”。但是,这些单词可能永远不会出现在同一句子中。 - Baz
它接下来走向哪个方向? - Baz
@Baz:这并不是真正提供解决方案的尝试,而只是为了展示/证明一个精确且高效的通用解决方案极不可能存在。 - Nuclearman
1
(是的,对于那些鲁莽的删除政策来说,这可能只是一条评论,但请下投票者放过它。) - David Eisenstat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接