同义词查找器算法

时间:2011-05-26 06:22:48

标签: php python algorithm synonym data-analysis

我认为例子会比loooong描述好得多:)

假设我们有一个数组数组:

("Server1", "Server_1", "Main Server", "192.168.0.3")
("Server_1", "VIP Server", "Main Server")
("Server_2", "192.168.0.4")
("192.168.0.3", "192.168.0.5")
("Server_2", "Backup")

每行包含同义词的字符串。由于处理了这个数组,我想得到这个:

("Server1", "Server_1", "Main Server", "192.168.0.3", "VIP Server", "192.168.0.5")
("Server_2", "192.168.0.4", "Backup")

所以我认为我需要一种递归算法。编程语言实际上并不重要 - 一般来说我只需要一点点帮助。我将使用php或python。

谢谢!

4 个答案:

答案 0 :(得分:6)

这个问题可以简化为图论中的一个问题,你可以在图中找到所有连接节点组。

解决此问题的有效方法是执行“泛洪填充”算法,该算法本质上是递归呼吸首次搜索。这个wikipedia entry描述了泛洪填充算法以及它如何应用于解决查找图的连通区域的问题。

要了解如何将原始问题转化为图表上的问题:将每个条目(例如“Server1”,“Server_1”等)作为图表上的节点。当且仅当它们是同义词时,用边连接节点。如果有足够的内存,矩阵数据结构特别适合跟踪边缘。否则,像地图这样的稀疏数据结构将起作用,尤其是因为同义词的数量可能会受到限制。

  • Server1是节点#0
  • Server_1是节点#1
  • Server_2是节点#2

然后edge [0] [1] = edge [1] [0] = 1,表示节点#0和#1之间存在边缘(这意味着它们是同义词)。 edge [0] [2] = edge [2] [0] = 0,表示Server1和Server_2是不是同义词。

复杂性分析

创建此数据结构非常有效,因为单个线性传递查找字符串到节点编号的映射就足以创建它。如果将字符串的映射存储到字典中的节点编号,那么这将是O(n log n)步骤。

执行泛洪填充是O(n),您只访问图中的每个节点一次。所以,算法都是O(n log n)。

答案 1 :(得分:3)

引入整数标记,表示同义词组。在开始时,会标记从1N的所有不同标记的单词。

然后搜索您的收藏集,如果您发现索引为ij的两个单词是同义词,则会将标记为ij的所有单词标记为较小两者的数量。在N次迭代后,您将获得所有同义词组。

这是一些肮脏且不是非常有效的解决方案,我相信使用union-find结构可以获得更多性能。

答案 2 :(得分:1)

编辑:这可能不是解决问题的最有效方法。如果您对最高性能感兴趣(例如,如果您有数百万的值),您可能会对编写更复杂的算法感兴趣。


PHP,似乎正在运行(至少使用给定示例中的数据):

$data = array(
    array("Server1", "Server_1", "Main Server", "192.168.0.3"),
    array("Server_1", "VIP Server", "Main Server"),
    array("Server_2", "192.168.0.4"),
    array("192.168.0.3", "192.168.0.5"),
    array("Server_2", "Backup"),
);

do {
    $foundSynonyms = false;
    foreach ( $data as $firstKey => $firstValue ) {
        foreach ( $data as $secondKey => $secondValue ) {
            if ( $firstKey === $secondKey ) {
                continue;
            }
            if ( array_intersect($firstValue, $secondValue) ) {
                $data[$firstKey] = array_unique(array_merge($firstValue, $secondValue));
                unset($data[$secondKey]);
                $foundSynonyms = true;
                break 2; // outer foreach
            }
        }
    }
} while ( $foundSynonyms );

print_r($data);

输出:

Array
(
    [0] => Array
        (
            [0] => Server1
            [1] => Server_1
            [2] => Main Server
            [3] => 192.168.0.3
            [4] => VIP Server
            [6] => 192.168.0.5
        )

    [2] => Array
        (
            [0] => Server_2
            [1] => 192.168.0.4
            [3] => Backup
        )

)

答案 3 :(得分:1)

这将比PHP示例(Python 3)产生更低的复杂性:

a = [set(("Server1", "Server_1", "Main Server", "192.168.0.3")),
    set(("Server_1", "VIP Server", "Main Server")),
    set(("Server_2", "192.168.0.4")),
    set(("192.168.0.3", "192.168.0.5")),
    set(("Server_2", "Backup"))]

b = {}
c = set()
for s in a:
    full_s = s.copy()
    for d in s:
        if b.get(d):
            full_s.update(b[d])
    for d in full_s:
        b[d] = full_s
    c.add(frozenset(full_s))

for k,v in b.items():
    fsv = frozenset(v)
    if fsv in c:
        print(list(fsv))
        c.remove(fsv)