【问题标题】:graph: find subgraphs using list of nodes including wildcards图:使用包括通配符的节点列表查找子图
【发布时间】:2012-02-17 20:49:15
【问题描述】:

以下问题有名称吗?是否有解决它的算法? : 给定一个图,无论有向还是无向,找到满足给定规范的所有路径

  1. 精确节点列表,或
  2. '*?'仅表示“任何节点或根本没有节点”,或
  3. '*{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.

谢谢

附言 有谁知道在 R 或 perl 或 C 中执行此操作的图形库?

【问题讨论】:

  • 这就是我能找到的全部内容:vldb.org/conf/1989/P185.PDF
  • 路径必须是简单路径吗?或者他们可以有周期吗?
  • 有循环意味着无限的解决方案。
  • 感谢@Diego 的链接。他们被允许有循环,但如果你到达最后的跳数!=隐含的那么它不是有效的路径。如果规范在包含固定开始和结束的部分之间分解,问题会简化,例如A -> *{1} -> D 和 D -> *? -> E. 并且可以解决。我现在最大的问题是将我的轨迹记录在这样一种数据结构中,这种数据结构很容易从失败的尝试中回溯。顺便说一句,我决定使用 Boost 的 Graph。一棵树是回溯的理想选择,但不幸的是在 boost 中没有这样的东西。

标签: algorithm graph path wildcard


【解决方案1】:

我不知道任何图书馆,但你必须把它分成两部分:

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

对于解析,我让你找到你需要做的事情(使用解析库或你自己)

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

算法的唯一问题是使用无限数量或有限数量的中间节点找到从节点 A 到节点 B 的所有路径。您可以使用动态编程或搜索算法(例如 DFS 或 BFS)来做到这一点。

【讨论】:

    【解决方案2】:

    我最后做的是:

    1. 问题是在 2 个节点之间找到所有长度为 N 的路径。不包括循环。
    2. 以边缘列表的形式读取数据,例如成对的 from->to 节点(假定节点的名称是唯一的)
    3. 创建一个哈希表(或 boost 和 stl、c++ 中的 unordered_map),其中节点名称作为键,哈希表作为值。
    4. 第二个哈希表将包含第一个节点指向的所有节点作为键。

    例如

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

    在将所有数据作为“edgelist”读入后,以 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 参数的节点的数组 ref。

    搜索两个节点之间的路径需要递归使用上述函数。

    例如

    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

    最棘手的部分是读取结果并提取每个路径的节点。经过深思熟虑,我决定使用 Tree::Nary(n-ary 树)来存储结果。最后我们有以下树:

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

    为了提取所有路径,请执行以下操作:

    1. 找到所有叶节点
    2. 从每个叶节点开始,通过其父节点向后移动到根节点并保存节点名称。

    以上是使用 perl 实现的,但在 C++ 中也使用 boost::unordered_map 实现哈希表。我还没有在 C++ 代码中添加树结构。

    结果:对于 3281415 个边和 18601 个唯一节点,perl 需要 3 分钟才能找到 A->'*'->'*'->B。准备好后我会更新 C++ 代码。

    【讨论】:

    • 哦,顺便说一句,读取一个大文件本身也是一个主题。文件格式是成对的节点名称,由它们自己的一行上的空格分隔。在 perl 中,可以逐行读取,然后在读取时拆分每一行。首先读取内存中的文件,然后通过正则表达式循环需要大致相同的时间。在 C++ 中,我使用 boost::split 将一行拆分为节点名称。逐行读取文件(使用 C 的 fopen 和 fgets)比在内存中读取文件(使用 C 的 read())然后使用 boost::split 在内存中拆分文件要慢一些,大约慢 10%。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-14
    • 1970-01-01
    • 1970-01-01
    • 2019-03-23
    • 1970-01-01
    • 2011-07-24
    相关资源
    最近更新 更多