【问题标题】:Compare two sentences word by word and return the number of word matches considering different word forms逐词比较两个句子并返回考虑不同词形的词匹配数
【发布时间】:2020-01-20 18:25:56
【问题描述】:

感谢Nina 我有一个代码可以逐字比较两个句子并返回匹配的单词数,如下所示:

function includeWords(wanted, seen) {
    var wantedMap = wanted.split(/\s+/).reduce((m, s) => m.set(s, (m.get(s) || 0) + 1), new Map),
        wantedArray = Array.from(wantedMap.keys()),
        count = 0;

    seen.split(/\s+/)
        .forEach(s => {
            var key = wantedArray.find(t => s === t || s.length > 3 && t.length > 3 && (s.startsWith(t) || t.startsWith(s)));
            if (!wantedMap.get(key)) return;
            console.log(s, key)
            ++count;
            wantedMap.set(key, wantedMap.get(key) - 1);
        });

    return count;
}

let matches = includeWords('i was sent to earth to protect you introduced', 'they\'re were protecting him i knew that i was aware introducing');

console.log('Matched words: ' + matches);

代码运行良好,但仍有一个问题:

如果我们也想返回匹配 introducedintroducing 怎么办?

【问题讨论】:

  • 这很模糊:“introduced”是否匹配“introduction”、“introductory”、“intro”……?都有些什么样的规矩?您是否有一个应被视为“相同”的单词的封闭列表?
  • 你可能有一些代码可以检查单词的常见共轭词尾,比如.*(ed|ing)...但是考虑到英语是多么古怪,我想你最终会得到非常复杂的代码处理所有异常的异常。
  • 研究Levenshtein距离/算法
  • 考虑到protectingprotected,在protectingprotected之间有7个字母匹配(考虑字母顺序)......如果我们包含这些单词的一半以上它们的长度与其他单词匹配!!!
  • @Dexygen 不是一个坏主意...但它使代码太慢...

标签: javascript


【解决方案1】:

如果您希望程序将“introduce”和“introducing”这两个词视为匹配,则相当于“模糊”匹配(非二进制逻辑)。一种简单的方法是需要更多代码,其算法可能类似于

  1. 取2个你想匹配的词,分词成有序列表 字母
  2. 比较各个字母的位置,即 匹配 a[0]==b[0]? a[1]==b[1] 其中 a[0] 代表第一个字母 的第一个词和 b[0] 表示第一个标记 字母/字符潜在匹配候选
  3. 保留此类位置匹配的滚动数字计数。在本例中为 8(介绍)。
  4. 除以 a = 8/9 的字长称之为 f
  5. 除以 b = 8/11 的字长称此为 g

  6. 提供一个阈值,超过该阈值程序将认为匹配。例如。如果你在 f 和 g 中说任何高于 70% 的内容都可以 被认为是一场比赛 - 中提琴,你有你的答案!

请注意,还需要进行一些规范化以防止低长度单词成为误报。您可以添加一个约束,即上述计算适用于至少有 5 个字母的单词(或类似的东西!

希望这会有所帮助!! 问候, SR

【讨论】:

  • 这是您的想法还是您有参考?我有兴趣阅读一些内容:感觉短词可能会失败:quick, quickies 具有相同的词根,但 ies 代表原始词的 30%,但允许这样的数量并不总是好的:rack/racket,或 for更长的词:财产/财产。无论如何,如果你有一些参考我会很高兴
  • 这纯粹是基于我的想法 - 没有可用的参考资料 - 抱歉 :)
【解决方案2】:

您可以计算一个单词对的相似度,并根据给定单词的长度和所需的模式来获得有多少字符相似的关系。

function getSimilarity(a, b) {
    var i = 0;
    while (i < a.length) {
        if (a[i] !== b[i]) break;
        i++;
    }
    return i / Math.max(a.length, b.length);
}

console.log(getSimilarity('abcdefghij', 'abc'));         // 0.3
console.log(getSimilarity('abcdefghij', 'abcdef'));      // 0.6
console.log(getSimilarity('abcdefghij', 'abcdefghij'));  // 1

console.log(getSimilarity('abcdef', 'abcdefghij'));      // 0.6
console.log(getSimilarity('abcdefghij', 'abcdef'));      // 0.6

console.log(getSimilarity('abcdefghij', 'xyz'));         // 0
console.log(getSimilarity('introduced', 'introducing')); // 0.7272727272727273

【讨论】:

    【解决方案3】:

    这是一个快速修复解决方案。

    它并不是一个完整的解决方案。
    由于英语有很多怪癖,几乎需要人工智能来理解该语言。

    首先添加一个可以比较两个单词并返回布尔值的函数。

    它还可以更轻松地测试特定单词,并适应真正需要的内容。

    例如,这是一个执行已使用的简单检查的函数。
    加上“...ed”与“...ing”检查。

    function compareWords (word1, word2) {
        if (word1 === word2) 
            return true;
        if (word1.length > 3 && word2.length > 3) {
            if (word1.startsWith(word2) || word2.startsWith(word1)) 
                return true;
            if (word1.length > 4 && word2.length > 4) {
              	if (/(ing|ed)$/.test(word1) && word1.replace(/(ing|ed)$/, 'inged') === word2.replace(/(ing|ed)$/, 'inged'))
              	    return true;
           }
      }
    
      return false;
    }
    
    //
    // tests
    //
    let words = [
      ["same", "same"],
      ["different", "unsame"],      
      ["priced", "pricing"],
      ["price", "priced"],
      ["producing", "produced"],
      ["produced", "producing"]
    ];
    
    words.forEach( (arr, idx) => {
      let word1= arr[0]; 
      let word2= arr[1];
      let isSame = compareWords(word1, word2);	    
      console.log(`[${word1}] ≈ [${word2}] : ${isSame}`);
    });

    然后在你已有的代码中使用它。

    ...
        seen.split(/\s+/)
            .forEach(s => {
                var key = wantedArray.find(t => compareWords(t, s));
    ...
    

    关于字符串相似度,这里是 f.e.一个较旧的 SO 帖子,其中包含一些比较字符串的方法:Compare Strings Javascript Return %of Likely

    【讨论】:

      【解决方案4】:

      我已经实现了这个,它似乎工作正常。任何建议将不胜感激..

      let speechResult = "i was sent to earth to introducing protect yourself introduced seen";
      let expectSt = ['they were protecting him knew introducing that you i seen was aware seen introducing'];
      
      // Create arrays of words from above sentences
      let speechResultWords = speechResult.split(/\s+/);
      let expectStWords = expectSt[0].split(/\s+/);
      
      function includeWords(){
      // Declare a variable to hold the count number of matches    
      let arr = [];    
      for(let a = 0; a < speechResultWords.length; a++){
              
          for(let b = 0; b < expectStWords.length; b++){
                
              if(similarity(speechResultWords[a], expectStWords[b]) > 69){
                 arr.push(speechResultWords[a]);
                 
                 console.log(speechResultWords[a] + ' includes in ' + expectStWords[b]);
      
              }
                    
          }  // End of first for loop  
          
      } // End of second for loop
          let uniq = [...new Set(arr)];
          return uniq.length;
      };
      
      let result = includeWords();
      console.log(result)
      
      
      // The algorithmn
       function similarity(s1, s2) {
              var longer = s1;
              var shorter = s2;
              if (s1.length < s2.length) {
                  longer = s2;
                  shorter = s1;
              }
              var longerLength = longer.length;
              if (longerLength == 0) {
                  return 1.0;
              }
              return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength)*100;
          }
      
          function editDistance(s1, s2) {
              s1 = s1.toLowerCase();
              s2 = s2.toLowerCase();
      
              var costs = new Array();
              for (var i = 0; i <= s1.length; i++) {
                  var lastValue = i;
                  for (var j = 0; j <= s2.length; j++) {
                      if (i == 0)
                          costs[j] = j;
                      else {
                          if (j > 0) {
                              var newValue = costs[j - 1];
                              if (s1.charAt(i - 1) != s2.charAt(j - 1))
                                  newValue = Math.min(Math.min(newValue, lastValue),
                                      costs[j]) + 1;
                              costs[j - 1] = lastValue;
                              lastValue = newValue;
                          }
                      }
                  }
                  if (i > 0)
                      costs[s2.length] = lastValue;
              }
              return costs[s2.length];
          }
          

      【讨论】:

      • 您应该在某处写下您使用 levenshtein 作为字符串指标(如果是这样的话)。有很多others。您可以比较它们(如果不是为了性能,也是为了好奇)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-24
      • 1970-01-01
      • 2015-03-13
      • 2011-01-10
      • 2013-04-27
      • 2016-02-05
      相关资源
      最近更新 更多