【问题标题】:How to find Relationships between Objects如何找到对象之间的关系
【发布时间】:2015-08-24 19:31:26
【问题描述】:

对于有类似问题的人(在找到解决方案后写):

根据下面的答案,您可能会注意到这个问题有很多不同的解决方案。我只选择了 Evan 的,因为它是我在自己的代码中实现的最简单的方法。但是,根据我的尝试,其他所有答案也都有效。 @SalvadorDali 链接了这个Kaggle page,这绝对很有趣,如果您有兴趣,我建议您阅读。 Prolog 也被提出作为一个可能的解决方案,我不熟悉它,但如果你已经知道它 - 它可能值得考虑。此外,如果您只想获取代码以使用下面的 Javascript 和 Python 示例。但是,每个人都有不同的解决方案方法,我不确定哪种方法最有效(请自行测试)。

更多方法/阅读:

http://en.wikipedia.org/wiki/Breadth-first_search

Prolog and ancestor relationship

https://www.kaggle.com/c/word2vec-nlp-tutorial/details/part-2-word-vectors


抱歉标题令人困惑,我无法找到正确表达我的问题的方法 - 欢迎任何更好的想法。

因为我很难描述我的问题,所以我会尽可能多地解释我的目标和代码:

注意:我这里的代码是 Go,但我也很乐意提供其他语言的答案,如果您有任何问题,我会尽快回答

基本上,我有一个“Word”对象数组,如下所示:

type Word struct{
     text     string
     synonyms []string
}

这是数组中 4 个单词的示例:

  []Word{
      {text: "cat" synonyms: ["feline", "kitten", "mouser"]}
      {text: "kitten" synonyms: ["kitty", "kit"]} 
      {text: "kit" synonyms: ["pack", "bag", "gear"]}
      {text: "computer" synonyms: ["electronics", "PC", "abacus"]}
   }

我的挑战是编写一种方法来测试两个单词之间的关系。当然,在上面的示例中,在 "cat""kitten" 之类的两个词之间进行测试会很容易。我可以检查“Cat”的同义词列表并测试它是否包含“kitten”。使用这样的代码:

areWordsRelated(word1 Word, word2 Word) bool{
    for _, elem := range word1.synonyms{
         if elem == word2.text{
             return true
         }
    }
    return false
}

但是,我不知道如何测试更远的关系。

例如:

areWordsRelated("cat","pack") //should return true 
//because "cat" is related to "kitten" which is related to "pack"
areWordsRelated("cat", "computer") //should return false

我尝试递归地执行此操作,但我所有的尝试似乎都不起作用。任何示例代码(我的代码在 Go 中,但 Python、Java 或 Javascript 也可以)、伪代码或只是解释都会非常棒。

【问题讨论】:

  • 按照你构建它的方式,你基本上会有多个有限的相关词集(一组是 {computer, electronics, PC, abacus},另一个是 {feline, kitten, cat ,鼠标,包,齿轮,包等等})。为什么不提前编写代码来定义所有这些集合,然后测试这两个词是否都是任何一个集合的成员?
  • 这似乎非常适合 Prolog
  • 代码从同义词库中读取所有单词,因此每个单词都有不同数量的同义词。此外,如果该方法可以检查不同“距离”之间的关系,那将是理想的,这样它就可以找到可能相隔 5 个(或更多)单词的关系。
  • 在这里,我在 Prolog 中找到了一个相关问题,您可以根据自己的问题进行调整:stackoverflow.com/questions/8966488/…
  • @DiogoDoreto Progol 是 declarative 语言,而 Go、Python 和 JavaScript 都是 imperative...所以我不推荐这种解决方案,只是出于好奇。跨度>

标签: javascript python arrays recursion go


【解决方案1】:

如果你给我一些反馈,我可以编辑它,因为它没有完全按照你的要求做,但它是 jist。我将通过技术解释对必须更改的内容进行编辑以符合您的确切示例。

package main

import "fmt"

func main() {
    words := []Word{
            {text: "cat", synonyms: []string{"feline", "kitten", "mouser"}},
            {text: "kitten", synonyms: []string{"kitty", "kit"}} ,
            {text: "kit", synonyms: []string{"pack", "bag", "gear"}},
            {text: "computer", synonyms: []string{"electronics", "PC", "abacus"}},
    }

    fmt.Println(areWordsRelated(words, words[0], words[2]))
    fmt.Println(areWordsRelated(words, words[0], words[3]))
}

type Word struct{
     text     string
     synonyms []string
}

func areWordsRelated(words []Word, word1, word2 Word) bool {
    for _, elem := range word1.synonyms{
        if elem == word2.text{
            return true
        } else {
            for _, word := range words {
                if word.text == elem {
                    if (areWordsRelated(words, word, word2)) {
                        return true
                    }
                }
            }
        }
    }
    return false
}

编辑:这并不完全符合您的要求,因为它没有在“pack”和“cat”之间建立联系,因为 pack 不是由实际的 word 对象表示的,我将接收 word2 的方法定义为对象(只是处理你的例子)。我可以改为将其设为一个字符串,以便它可以在返回之前检查“kit”的同义词数组中的“pack”,但这个想法仍然是一样的......这是算法的高级解释。

迭代同义词,如果不匹配,则在原始集合中找到 Word 对象并将其作为第一个参数调用我自己。这将递归地耗尽每条路径,直到找到匹配项,或者没有任何剩余,在这种情况下,您在循环之外返回 false。上面的代码在 go playground 中运行并正确返回 true\nfalse。请注意,递归调用是在 if 中进行的,以防止过早返回 false(这也是一种性能增强,因为我们会在找到 true 时立即返回,而不是继续递归路径)。

https://play.golang.org/p/gCeY0SthU1

【讨论】:

  • 有没有办法让它在远离起始词 X 个词后超时?我可以在方法中添加一个参数来跟踪迭代对吗?
  • @MaximilianSun 是的,这很容易,只需添加一个计数器并在循环中检查它。如果您达到边缘限制或您所称的任何内容,则返回 false。
  • 我认为您关于“pack”与“kit”无关的注释主要是我的示例而不是您的代码的问题。
  • @MaximilianSun 是的,这个例子并不一致,所以我只是选择这样做。如果您将word2 设为字符串而不是Word,然后根据需要修改函数体以进行编译,您将能够传入words[0] 和“pack”,它将返回true
【解决方案2】:

Python 解决方案:

class Word:

   # Dictionary of Words, keyed by name.
   word_dict = {}

   def __init__(self, name, synonyms):
      self.name = name
      self.synonyms = synonyms

      # Update the dictionary.
      Word.word_dict[name] = self
      for s in synonyms:
         if not s in Word.word_dict:
            Word.word_dict[s] = Word(s, [])

   def isAncestor(self, other):
      if other in self.synonyms:
         return True
      for s in self.synonyms:
         if Word.word_dict[s].isAncestor(other):
            return True
      return False

def areWordsRelated(word1, word2):
   if not word1 in Word.word_dict or not word2 in Word.word_dict:
      return False
   return Word.word_dict[word1].isAncestor(word2) or Word.word_dict[word2].isAncestor(word1)

words = []
words.append(Word("cat", ["feline", "kitten", "mouser"]))
words.append(Word("kitten", ["kitty", "kit"]))
words.append(Word("kit", ["patck", "bag", "gear"]))
words.append(Word("computer", ["electronics", "PC", "abacus"]))

print(areWordsRelated("cat", "kit"))
print(areWordsRelated("kit", "cat"))
print(areWordsRelated("cat", "computer"))
print(areWordsRelated("dog", "computer"))

输出:

True
True
False
False

【讨论】:

  • 我将如何构建树?
【解决方案3】:

首先不清楚你如何在这里定义关系。如果你的 "cat" 有同义词:["feline", "kitten", "mouser"],这是否意味着 "mouser" 有同义词 "cat"。

根据我的理解,答案是否定的。所以这里是python中的一个解决方案:

G = {
    "cat": ["feline", "kitten", "mouser"],
    "kitten": ["kitty", "kit"],
    "kit": ["pack", "bag", "gear"],
    "computer": ["electronics", "PC", "abacus"]
}

def areWordsRelated(G, w1, w2):
    if w1 == w2:
        return True

    frontier = [w1]
    checked = set()
    while len(frontier):
        el = frontier.pop()
        if el in G:
            neighbors = G[el]
            for i in neighbors:
                if i == w2:
                    return True
                if i not in checked:
                    frontier.append(i)
                    checked.add(i)

    return False

areWordsRelated(G, "cat", "pack") #true
areWordsRelated(G, "cat", "computer") #false

那么我们在这里做什么?起初你有你的图表,它只是显示你的关系的字典(地图)(我基本上拿走了你的切片)。

我们的算法像模具一样生长,维护一组检查元素和当前边界。如果 frontier 为空(没有可探索的内容,则元素未连接)。我们一次从边界中提取一个元素并检查所有邻居。如果它们中的任何一个是我们正在寻找的元素 - 那么就有联系。否则检查我们是否已经看到了这样的元素(如果没有,则将其添加到边界和已检查的集合中)。

请注意,如果您的关系运作方式略有不同,您只需修改图表即可。


最后一句话,如果您正在寻找一种正常的方法来查找同义词,请查看word to vector algorithm 和一个不错的implementation in python。这将允许您找到非常复杂的关系,即使在没有指定这种关系的情况下也能发现 CaliforniaGolden Gate 是相关的。

【讨论】:

    【解决方案4】:

    您正在寻找二级关系(与您已经知道如何找到的“简单”一等示例相反),这意味着您必须做以下两件事之一:

    (1) 存储密集型解决方案需要维护一个单独的 2 度关系列表,然后简单地在该(更长的)列表中进行搜索 - 这需要维护(可能更多)关于单词关系的更多数据。例如,如果您有 10000 个单词,每个单词大约有 10 个同义词,则存储了 100,000 个一级关系。但是那样你就会有十亿个二级关系。当然,这很快就会变得笨拙。

    在这种情况下,每个条目如下所示: {文本:“猫”同义词:[“feline”,“kitten”,“mouser”]秒:[“pack”,...]} ...您只需编写一个单独的函数来检查“同义词”或“秒”中的关系。

    (2) 程序化解决方案仍然是只存储 1 度关系,然后执行嵌入式循环。

    在这种情况下:

    //// This checks for 1st degree relationship
    areWordsRelated1(word1 Word, word2 Word) bool{
        for _, elem := range word1.synonyms{
             if elem == word2.text{
                 return true
             }
        }
        return false
    }
    
    //// This checks for 2nd degree by checking 1st and then, if not, 
    //// then trying the 1st degree function on the children of word2
    //// before giving up and returning false
    areWordsRelated2(word1 Word, word2 Word) bool{
        for _, elem1 := range word1.synonyms{
             if elem1 == word2.text{
                 return true
             } else {
             for _, elem2 := range elem1.synonyms{
                 if areWordsRelated1(word1, elem2) {
                     return true
                 }
             }
        }
        return false
    }
    

    注意:我注意到在您的示例数据中,“猫”与“小猫”相关,但“小猫”与“猫”没有相反的关系。

    【讨论】:

      【解决方案5】:

      这是一个用 JavaScript 编写的递归算法示例,其中包含一些 jQuery 以使搜索数组更容易。它可能会被优化,但应该给你一些东西。

      $(function() {
        var words = [{
          text: "cat",
          synonyms: ["feline", "kitten", "mouser"]
        }, {
          text: "kitten",
          synonyms: ["kitty", "kit"]
        }, {
          text: "kit",
          synonyms: ["pack", "bag", "gear"]
        }, {
          text: "computer",
          synonyms: ["electronics", "PC", "abacus"]
        }];
      
        console.log(areWordsRelated('cat', 'pack', words));
        console.log(areWordsRelated('cat', 'rack', words));
      });
      
      function areWordsRelated(parentWord, childWord, list) {
        var parentWordItems = $.grep(list, function(element) {
          return element.text === parentWord;
        });
      
        if (parentWordItems.length === 0) {
          return false
        } else {
          var parentWordItem = parentWordItems[0];
          var remainingItems = $.grep(list, function(element) {
            return element.text !== parentWord;
          });
          if (parentWordItem.synonyms.indexOf(childWord) >= 0) {
            return true;
          } else {
            for (var i = 0; i < parentWordItem.synonyms.length; i++) {
              var synonym = parentWordItem.synonyms[i];
              if (areWordsRelated(synonym, childWord, remainingItems)) {
                return true;
              }
            }
            return false;
          }
        }
      }
      &lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"&gt;&lt;/script&gt;

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-02-08
        • 2011-03-08
        • 1970-01-01
        • 2021-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多