【问题标题】:Finding the "best" combination for a set为一组找到“最佳”组合
【发布时间】:2015-11-19 02:04:33
【问题描述】:

我有一个集合,sentences,它包含来自英语语言的字符串形式的句子。我希望创建sentencessentences2 的子集,其中包含仅包含 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"))

这里每个单词至少表示两次,如果我们在句子上使用计数器可以看到:

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,其中每个单词至少表示五次。

所以我追求所有可能的 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 的大小。

还请注意,我希望结果集中的单词可以在前一百个单词中找到(common_words 包含 100 个值)。这也是设计使然。

【问题讨论】:

  • 您可以通过在您的问题中添加Minimal, Complete, and Verifiable example 来使其更清楚。
  • 那么,您想要 20 个单词,其中每个单词出现在任何句子中的最小值是最高的吗?最好创建一个字典,将单词映射到它们在任何句子中的最少出现次数。
  • @Baz:一个词可以在一个句子中出现多次吗?这怎么算?我是否理解正确,如果您要为您的集合选择一个单词,您不会选择总出现次数最多的单词,而是选择出现最多句子的单词?一个句子必须满足什么条件才能符合设置的句子2?比示例更好的是给出一个exact 规范......我发现你的第一段很难理解。请您进一步澄清一下吗?
  • 指定你想要什么。目前,您的问题尚不清楚...
  • 您愿意设置 500 点赏金,但不上传您真正关心的输入?

标签: python algorithm statistics combinations linguistics


【解决方案1】:

我建议您尝试以下方法。我没有数学证据证明它会产生绝对最好的“分数”,或者确实是其他一些“善意的衡量标准”, 但我确实相信它可能会对您有所帮助,当然,除非案例要求您实际证明并找到绝对最好的。

我不会说python,但是策略和随后的实现很简单,甚至不需要伪代码来解释。 不过我会用很多词,不是因为它很复杂,而是要清楚(我希望)。

您将需要一种方法对矩阵进行对角化,或者至少找到与最大特征值对应的特征向量。 并非每种语言都提供本地方法。在 C++ 中,您可能会使用 Eigen 库。在我曾经测试过的 C# 中,我连接了 MathNet。在python中,我不知道。 对角化/特征向量工具的要求是​​有限的:矩阵总是实数、对称的,所有元素都是正数。 然而,尽管对角线元素在其行/列中至少与其他任何元素一样大,但矩阵肯定不是“对角线占优”。

您提出的问题可以抽象地表述为整数,而不是单词,这使得(对我来说......)更方便地制定和实现 (但除此之外,它是完全不相关的)。 事实上,我们只需要您的“common_words”,因此需要 100 个整数 [0..99],并且您只需将单词放入数组或列表中并使用它们的索引,就可以设置单词和整数之间的映射。

附带说明:对于您的语言应用程序,该策略可能(远)更适用于您可能构建“恶意”困难输入的整数的完全广义问题。 原因是我们将基本上利用成对相关性,如果有的话,在项目之间(同一个句子中的单词),而三元组、四元组的强相关性……可能会降低效率(但我们会为三胞胎做点什么)。 显然,在格式良好的语言中,您会期望相关性:“am”通常与“I”一起出现, 虽然“have”总体上可能比“has”更频繁,但一旦你找到“he”,你可能更有可能在同一个句子中得到“has”而不是“have”。以此类推。

一些定义是有序的。

NCommon 是 common_words 中元素的数量(即 100)。 common_words “是”整数 [0..NCommon-1]。

NMaxSet 是您最终愿意使用的问题限制(即 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 是(成对)相关矩阵,告诉您在基础句子集 S 中出现 {j,k} 组合的可能性有多大。

(总是,在处理此类矩阵时:如果最后碰巧有空列(因此行):删除这些列和行, 同时删除作为矩阵基础的过滤器中的相应条目,显然它不起作用。 此后我们将假设这已经完成(除非另有说明),即矩阵中没有一列/行是空的。)

二。计算结果(主要方法,我们将在下面细化):

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

设置 NTarget=NSetMax

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

通过新过滤器运行 S0 以生成 STarget。 计算所有“单词”的出现次数,找到它们的最小值:这就是你的“设定质量”值。 例如,您可以通过计算关联的 MTarget 并扫描对角线值 MTarget[k,k] 来这样做。 这似乎涉及不必要的额外工作,因为您还要计算非对角线元素,但我们会看到 MTarget 在后续改进中可能会派上用场。

三。改进。

A) 我们需要验证是否通过从过滤器 F(NsetMax) 中删除一个或几个项目,将其减少到小于 NSetMax 的某个 NTarget,我们会得到更好的分值。 这需要注意:删除两个(或三个,...)项目很可能会提高分数,但删除其中任何一个都会降低分数。

让我们对这个问题进行第一次(并且相当合理)的尝试。

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

再次过滤(作为输入,您当然可以现在使用 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 的情况,并调查是否应该以一次一次的方法进一步减少它。 (同样的情况也适用于 (B) 的最后阶段,如果从上方接近 NSetMax 时,一旦接近 NSetMax,就“一次一个”。)

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

假设我们有一些 NTarget,一个关联的过滤器 FTarget 和矩阵 MTarget。 (过滤器中项目('words')的顺序总是(当然)等于矩阵中行和列的顺序。)

从 MTarget 我们可以直接推断出一些关于如果我们从过滤器中删除第 j 个项目会发生什么的信息。 当然,矩阵中的第 j 行和第 j 列变为空(全为零)。 更有趣的是,M[j,k] 表示在所有包含项目 j 的句子中项目 k 出现的次数。 因此,当消除所有 j 时(通过从过滤器中删除它),我们预先知道在得到的新矩阵 MReducted 中, 元素 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], 这是计算 ALSO(“随后”)删除 k 将如何改变 [p,p] 所需要的。 顺便说一句,无论是先删除 j 再删除 k 或反之亦然,或同时删除两者,最终结果都必须是无关紧要的。

为此,我们需要某种“衍生物”。 幸运的是,我们可以计算和处理它,而不需要太多的计算工作(鉴于 NTarget 已经相当小,大约 20 个)。

考虑一个与当前过滤器F相关的'reductionfilter' G(F; j),简单地定义为处理F但忽略其中的项目j。 对于 G,我们以与往常相同的方式计算“归约矩阵”N(G),为了方便讨论(和实现),我们保持相同的大小(不删除空列/行)。 当然,在这样的 N 矩阵中,第 j 行第 j 列是空的。 它的对角元素将具有如上所述的值(我们可以直接从 M 计算这些值)。 但是,现在我们也有所有由移除 j 产生的非对角元素。

从 N(G{F;j}) 我们现在可以确定如果我们删除 ALSO 项目 k 会发生什么,请参阅上面关于如何从当前矩阵中获得预期分数的详细说明。 因此,这允许在移除 {j,k} 对时计算分数。

如果 setsize 为 20,我们必须计算 20 个 N(G(F;j)) 矩阵,我认为这是一个相对较小的工作(您的句子集合现在也将比原来小很多)。 然后,拥有所有 N,为 20*19/2 唯一对中的每一个计算得到的对移除分数,你就可以 选择要从过滤器中删除的 PAIR(而不是单个元素)。 您可以在运行中将其与“一次一个”的减少进行比较,并做出适当的选择,如何系统地减少过滤器以寻求更好的分数。 有很多方法可以进行这种比较。一个相对简单的方法是:先计算一对,然后再计算一个(总共 3 个元素)的减少。 与先删除一个然后删除一对进行比较。 选择这两者中最好的,但只从该选择中执行第一步(单个或一对)并重复该过程。

请注意,使用这些“派生”过滤器 G、矩阵 N 和随后的分数预计算,如所述, 您有效地引入了 TRIPLES 项目之间的相关性:您确定所有 {j,k,p} 在删除 j 和 k 时对 p 的频率会发生什么。 当然,这个想法可以扩展到合并四倍甚至更多,但是(a)我不相信它实际上会有很大帮助,并且(b)你在这条路上走得越远,计算量就会增加得越快. 我什至怀疑这里解释的引入“三元组”是否相关, 但我不是语言专家,除了一些额外的编程工作外,没有什么大的缺点。

D) 该策略的主干是依靠具有最大特征值的特征向量来指向相关项目以进行后续过滤。 当然,可能会发生两个或多个特征值“几乎”相同,并且相应的特征向量可能指向完全不同的项目集 在分析它们最大的组成部分时。 在这种情况下,“分支”将是值得的,即:与其中一个一起工作,工作到最后,然后 与他们的竞争对手重做所有事情,看看他们是否会产生更好的结果。 如果您遇到很多分支(在解决问题的道路上的各个点),就会出现问题。这不太可能发生,但 如果确实发生,实施应该有一些策略以实际的方式处理它。 我建议您首先保留任何分支,但您确实要监视“竞争性”最大特征值的出现,以便向用户输出警告。 或者,您可以实现对此类分支的处理,但对程序认为“几乎相等”的内容非常严格,或者设置一些限制为 到要研究的(总)分支的数量,从而减少计算时间失控的可能性。

我不得不把它留在这里(因为时间不够......),希望我已经充分解释了这个想法,以及一些需要考虑的细节和改进。

我没有时间为自己组织相关的“真实语言”句子作为输入并针对它进行测试。 我已经在 C# 中编写了基本步骤,针对随机整数“句子”进行了测试,并带有一些偏见以迫使某些“单词”比其他“单词”更频繁地出现, 但不关心“句子”中“单词”之间的相关性。 结果对我来说似乎很合理,但我没有时间深入分析它。

鉴于我用作输入的“随机”序列缺乏结构,而真实语言预计会表现出显着的成对相关性, 该策略所利用的, 我很希望它可能对您有用。

[已编辑] 补充说明:在上面我偶尔会草率地谈论“j”、“k”等。对此我很抱歉。 有时“j”指的是某物的第 j 个条目(矩阵行,过滤器列表中的索引),有时它指的是(通常)过滤器中的相应 VALUE。 例如,一个过滤器可能包含 18 个项目,编号(索引)0..17,但它们的 VALUES 始终引用原始 Common_words 列表, 所以它们可能是 {3, 15, 29, 30, 31, 40, ...}。 然后,当它说“从过滤器中删除 j”时,通常意味着“从过滤器中删除第 j 个条目(并且该条目可能具有 [0..NCommon-1] 中的任何值)。 将过滤器应用于句子时,您将过滤器中的值与句子中的值进行比较。 我希望上下文 - 结合对推理路线的公平理解 - 总能清楚地说明真正的含义。

[编辑:一些测试结果] 我已经运行了我的 C# 实现,使用上述算法(或多或少:描述了大多数但不是所有改进/细节)来对它会产生什么有一些印象。

作为输入,我从 Gutenberg 项目中获取了 4 本书(纯文本)。总共(仅)27k 个句子,380k 个单词(20k 个不同),所以样本量相当小。

根据该输入确定的常用词列表以“the”、“of”、“and”、“to”开头...(按总输入中的出现频率排序)。

该算法过滤了 14 个单词(“i”、“what”、“do”、“you”、“it”、“yes”……)以产生“最优”“set-quality-value” 8 个(仅找到这 14 个单词的 139 个句子)。

由于我对只使用 100 个“常用词”的假设持怀疑态度,因此我先验地允许使用 500 个常用词,而不是 100 个,而且在进入最终过滤器的词中,肯定有 4 个频率编号高于 100(“yes”、“know”、“say”、“think”):例如,如果您要按所有输入中的出现对它们进行排序,“yes”在整体列表中排名第 224,大概是“常用词”的基础。

【讨论】:

    【解决方案2】:

    免责声明:您没有指定数据特征,所以我的回答将假设它不是太大(超过1,000,000个句子,每个最多1,000个)。描述也有点复杂,我可能没有完全理解这个问题。

    解决方案:
    与其关注不同的组合,不如为你最常用的 100 个单词创建一个 hashMap(dict in python),然后遍历句子数组,对每个句子中的每个单词,增加其对应的值(如果它已经在字典中)。
    最后,只需按照每个单词(key)的出现次数(value)对这个hashMap进行排序,然后使用最频繁的20个。
    复杂度:
    快速浏览一下算法,给出:
    遍历N个句子,每个用M个词遍历,增加hashMap值。最后对(单词,出现)对的数组进行排序。可以忽略不计(hashMap 大小不变,100 个常用词),提取前 20 个。
    时间复杂度:O(N*M)
    空间复杂度:O(1)(我们不需要存储句子,我们只需要 hashMap)

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

    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]
    

    顺便说一句,'he'和'she'没有出现在句子的任何地方,但是你说下面的单词组合得了5分

    (我,你,他,做,想,是,不,能,它,好,不能,但是,我,为什么,在哪里,现在,不,这里,她,是)

    我误解了这个问题吗?

    信用到期:StackOverflow: Sort a Python dictionary by value

    【讨论】:

    • 这不只是最初(而且相当简单)的部分,即确定哪些单词在所有输入中出现的频率最高?在我看来,从排序列表中选择前 20 位不一定是解决问题的方法。
    【解决方案3】:

    STEP 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
    

    理想的第 2 步 如果您很幸运并且您的 counts 数据类型包含 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 个独特的单词,在这种情况下,您必须满足于最佳猜测/近似值。我可能会做类似的事情,使用上面的代码,而不是 Pick 20 使用 Pick 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 的最终分数吗?从统计上讲,这取决于有多少独特的单词和句子,但可能不是。

    EDIT 实施说明和更正。与出现单词的句子 id 集相交将为您提供最低分数 减 1(零索引)。例如,“狗”在句子 1 和 2 中; “猫”在第 2 句和第 3 句中;“青蛙”在第 4 句中;[1,2] /\ [2,3] /\ [4] = [] 的交集,但最低分数为 1 result.Size() + 1. 以同样的方式,“Dog”和“Cat” [1,2] /\ [2,3] = [2] 的设置大小为 1,但最小值得分为 2。

    【讨论】:

    • @BurhanKhalid - 没错,它不是任何特定的编程语言,它是伪代码 - 一种类似于简化编程语言的符号,用于程序设计 - 表示算法步骤的更高级别的抽象.
    • @谢谢路易斯·里奇。你能解释一下这条线:score = combo.Reduce((prev, curr) => prev.ids.Intersect(curr.ids)).Size。什么是combo类型,什么是prev和curr?
    • @Baz - combo 是我在上面定义的 count 类型的元素的集合。 Reduce 是一个常见的 map/reduce 术语,用于获取集合并返回单个值。所以这行所做的就是遍历 combo 的所有元素(元素包含单词、count 和 ids 属性)并将所有 ids 集相交,这会将唯一索引集返回到 sentences i> 包含所有单词,大小就是你的分数。
    【解决方案4】:

    我会寻找一种数据聚类形式,以加快每个 20 字试验的搜索速度。

    重要的数据是句子中的唯一词。

    如果jaccard距离很​​小,可以认为2个句子很接近。杰卡德距离为 1 - (size of(句子中词的交集))/(size of(句子中词的并集))。

    因为jaccard距离是度量的(满足三角不等式),所以可以建立一个m-tree索引,可以更快地找到句子。

    在此基础上可以进行聚类(最好的结果将彼此接近)。

    最后通过遍历每个句子,并找到 K 个最近的邻居,你可以找到一组最多 20 个可以使用的单词。这将为 K(共享最佳 20 个单词的句子)提供不同的值。

    我可能会使用数据库来支持 select unionselect intersection 允许设置测试

    【讨论】:

      【解决方案5】:

      我不确定是否有可能在不到指数的时间内提出最佳解决方案,问题可能没有足够的结构。但这里有一个启发式方法来提出一个“好的”解决方案。

      我认为这样做的方法是从大小为 0 的wordset 开始,然后以“聪明”的方式一个接一个地添加单词,最多 20 个。考虑给定的wordset_n,每个单词的分数只能在添加新单词时增加或保持不变。 wordset_(n+1) 得分低于wordset_n 的唯一方法是,如果第 (n+1) 个单词降低了最小值。所以我们限制自己只添加能够提高最小值或保持不变的单词(但请参阅下面的详细说明)。

      所以第一个近似值,

      1. 按频率在sentences 中对common_words 中的单词进行排序。
      2. 将最常出现的词添加到wordset,直到分数不为零。
      3. 搜索可能的wordsets 树,仅通过添加增加或保持前一个节点的分数(最大 N = 20)的单词来构建。所以沿着这棵树的路径将是wordset_nwordset_(n+1)、wordset_(n+2)`,每个节点由前一个节点加上一个新词组成。
      4. 选择得分最高的叶节点。

      在第二个近似值上,a) 您可能想为第 2 步尝试多种组合。100 选择 3 是 162000,这不是不可能的。 b) 此外,对于第 3 步,您可能希望向前看两个步骤,而不仅仅是一个步骤 - 即,如果在 所有 可能word n+2,分数还是低于wordset_n。如果存在反相关,这可能会有所帮助,即带有一个动词的短句不太可能包含另一个动词。最后 c) 如果这种方法在计算上仍然令人望而却步,您可以通过关闭第 (n+1) 个未将分数提高给定数量的分支来进一步限制树的构建。

      如果sentences 中的单词分布相对独立,或者仅表现出正相关,那么这种方法可能会让你得到非常接近最优的结果。

      【讨论】:

        【解决方案6】:

        通过 CLIQUE 减少的 NP-hard(假设我们用参数替换 20)。给定一个我们正在寻找 k-clique 的图,为每个顶点分配一个唯一的单词,使两个单词的句子对应于每条边,并尝试选择 k 选择 2 个包含每个单词 k - 1 次的句子。

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

        【讨论】:

        • 感谢您的回答。您介意为一些简单的句子添加示例吗?
        • 您能否更详细地解释您的答案,因为我正在努力理解它。假设我正在使用networkx,我应该为每个句子添加边到图中,如下所示: for word_pair in itertools.combinations(sentence,2): G.add_edge(word_pair[0],word_pair[1])
        • 再次感谢。 “使每个边缘对应的两个单词句子”是什么意思?您的意思是我应该将每个句子的所有“双词组合”添加到图表中。例如,句子“i am here”包括“两个单词的句子”:“i am”、“am here”、“i here”。因此,例如,顶点“am”将有两条边以“i am”和“am here”的形式进入它。我也不明白为什么派系在这里是相关的。例如,我的解决方案集可能包含单词“he”和“she”。然而,这些词可能永远不会出现在同一个句子中。
        • 那它往什么方向发展?
        • (是的,反对者,这可能是一条评论,但对于此类通信的鲁莽删除政策。)
        猜你喜欢
        • 1970-01-01
        • 2014-11-28
        • 1970-01-01
        • 2020-09-02
        • 1970-01-01
        • 2023-04-10
        • 1970-01-01
        • 2010-09-06
        • 2018-03-16
        相关资源
        最近更新 更多