图形:使用包含通配符的节点列表查找子图

5
以下问题有没有名称,是否有算法来解决? 给定一个图形,可以是有向或无向的,找到所有满足以下规范的路径:
  • 一个精确节点列表,或
  • '*?'表示'任何节点或根本没有节点',或
  • '* {n}'表示'任何n个连续连接的节点'
例如:
A -> B -> *? -> D which results in ABXD and ABYD and ABD etc.

或者

A -> *{1} -> D -> *? -> E which results in ABXDZE and ABYDZE and ABDZE etc. etc.

谢谢

p.s. 有没有人知道用R、Perl或C实现此功能的图库?


这是我找到的全部内容:http://www.vldb.org/conf/1989/P185.PDF - Diego
路径是否必须是简单路径?还是可以有环路? - templatetypedef
拥有循环将意味着无限解集。 - Faylixe
感谢@Diego提供的链接。它们允许有循环,但如果你在一定数量的跳跃后到达终点,则不是有效路径。如果规范被分解为包含坚实开头和结尾的部分,例如A -> * {1} -> D AND D -> *? -> E,则问题会简化。可以解决。我现在最大的问题是记录我的轨迹在这样一个数据结构中,这个数据结构易于从失败的尝试中回溯。顺便说一下,我决定使用Boost的图形库。树对于回溯来说是理想的,但不幸的是,在boost中没有这样的东西。 - bliako
2个回答

1

我在最后做的是:

  1. 问题是找到两个节点之间长度为N的所有路径。 排除循环。
  2. 将数据读入作为边列表,例如来自->到节点的一对(假定节点名称是唯一的)
  3. 创建一个哈希表(或在boost和stl, c++中无序_map)作为值的节点名称作为键的哈希表。
  4. 第二个哈希表将包含第一个节点导致的所有节点作为键。

例如:

A->B
A->C
B->D
C->E
E->D

在将所有数据作为“边列表”读入后,保存输入数据的Perl表示形式的结果数据结构如下:

my %hash = (
'A' => {'B' => 1, 'C' => 1},
'B' => {'D' => 1},
'C' => {'E' => 1},
'E' => {'D' => 1},
);

判断一对节点是否直接连接可以通过以下方式(使用Perl)进行粗略计算:

sub search {
    my ($from,$to) = @_;
    if( $to eq '*' ){ return defined($x=$hash{$from}) ? [keys $hash{$from}] : [] }
    return defined($x=$hash{$from}) && defined($x{$to}) ? [$to] : []
}

在上述函数中,通过将$to设置为'*',提供了返回所有与“from”节点连接的节点的功能。返回值是一个节点数组引用,这些节点直接连接到$from参数。
查找两个节点之间的路径需要递归使用上述函数。
例如:
sub path {
    my ($from,$to, $hops, $save_results) = @_;
    if( $hops < 0 ){ return 0 }
    $results = search($from, '*');
    if( "".@$results == 0 ){ return 0 }
    $found = 0;
    foreach $result (@$results){
        $a_node = new Tree::Nary($result);
        if( path($result, $to, $hops-1, $a_node) == 1 ){
            $save_results->insert($save_results, -1, $a_node);
            $found = 1;
        }
    }
    return $found;

}

如果深度不太大(即 $hops < 6 ?),使用递归是可以的,因为会出现堆栈溢出 [sic]。

最棘手的部分是阅读结果并提取每个路径的节点。经过多次考虑,我决定使用 Tree::Nary(n元树)来存储结果。最终我们得到以下树形结构:

     |-> B -> D
A -> |-> C -> E -> D

为了提取所有路径,请执行以下操作:
  1. 查找所有叶节点
  2. 从每个叶节点开始,通过其父节点向后移动到根节点并保存节点名称。
上述内容使用perl实现,但我也使用boost :: unordered_map进行哈希表的C ++实现。我还没有在C ++代码中添加树结构。
结果:对于3281415个边和18601个唯一节点,perl需要3分钟才能找到A ->'*'->'*'->B。准备好时,我会更新C ++代码。

顺便提一下,读取大文件也是一个独立的主题。文件格式是每行一个由空格分隔的节点名称对。在Perl中,逐行读取然后在读取时拆分每行是可以的。先将文件读入内存,然后通过正则表达式循环遍历所需时间大致相同。在C++中,我使用boost::split来将一行拆分为节点名称。逐行读取文件(使用C的fopen和fgets)比将其读入内存(使用C的read())并在内存中使用boost::split拆分要慢一些,大约慢10%。 - bliako

1

我不知道有没有相关的库,但你需要将其分为两部分:

  • 用户查询解析
  • 查找所需内容的算法

对于解析部分,你可以使用解析库或自己编写代码来完成。

关于算法部分,我建议你定义一个特殊的结构(如链表)来表示查询,其中每个元素可以表示一个实际节点、x个节点或无限数量的节点。

你需要解决的唯一问题是如何找到从节点A到节点B的所有路径,包括使用无限数量或有限数量的中间节点。你可以使用动态规划或搜索算法,如DFS或BFS来实现。


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