使用Perl

时间:2017-01-14 01:37:42

标签: perl graph graph-theory directed-graph

首先,我想澄清一点,我对图论和解析有向图的正确算法的经验很少,而且我在这里搜索了SO,但却找不到我想要的东西。希望你们能帮助我:)。

我有一个大的有向图(大约3000个节点),它有几个由连接节点组成的子图,子图不相互连接。这是我在这里的数据的一个小代表图:

directed graph with unconnected subgraphs

我正在编写一个Perl脚本来查找从每个源节点到sink节点的所有可能路径,并将它们存储在一个数组数组中。因此,对于此图,可能的路径是:

1,2,3,4,5,6
1,2,3,4,5,7
1,8,9,10
11,12,13
11,14
15,16,17

我在脚本中完成此搜索的方式是在以下步骤中使用Graph模块:

  1. 查找图表中的所有源节点并将其存储在数组中
  2. 查找图表中的所有汇聚节点并将其存储在数组中
  3. 使用Floyd-Warshall算法查找所有对短路径
  4. 如果存在源节点和汇聚节点之间的路径,则搜索APSP Floyd-Warshall图形对象。如果有路径,则将其存储在数组数组中。如果没有路径,则什么都不做。
  5. 以下是我的脚本中的一部分:

    #Getting all source nodes in the graph:
    my @source_nodes = $dot_graph->source_vertices();
    my @sink_nodes = $dot_graph->sink_vertices();
    
    # Getting all possible paths between from source to sink nodes in the graph:
    print "Calculating all possible overlaps in graph\n";
    my $all_possible_paths = $dot_graph->APSP_Floyd_Warshall();
    print "Done\n"; 
    
    # print "Extending overlapping contigs\n"; 
    my @all_paths;
    foreach my $source (@source_nodes) {
        foreach my $sink (@sink_nodes) {
            my @path_vertices = $all_possible_paths->path_vertices($source, $sink);
            my $path_length = $all_possible_paths->path_length($source,$sink);
    
            #Saving only the paths that actually exist:
            push (@all_paths, \@path_vertices) unless (!@path_vertices);
        }
    }
    

    问题在于它适用于小图形,但是现在我有3000个节点,它需要很长时间才能完成(假设每个路径需要1ms才能找到,需要312.5通过他们所有的日子)。我知道使用Floyd-Warshall算法找到图中所有可能的路径,只找到源和接收器之间的路径效率不高,但是当我编写脚本时,我需要尽快得到结果,而且我的图形要小得多。

    我的问题是如何找到从图中每个源开始的所有路径,这些路径将以汇聚节点结束,而不首先找到所有可能的路径?这就是所谓的广度 - 第一次还是深度优先搜索?如何使用Perl实现(如果可能的话,使用Graph模块)?任何帮助都会很棒!

    PS:为了让程序运行得更快,我开始尝试将初始大图分解为子图并运行原始脚本,但是使用{{3分叉搜索源和汇之间路径的主循环}}。你们怎么看待这种方法?

1 个答案:

答案 0 :(得分:3)

你对找到最短路径不感兴趣,所以忘记所有那些最短路径算法。

您有兴趣找到所有路径。这称为树遍历,可以先执行深度优先或广度优先。由于您遍历整个树,因此采用哪种方法并不重要。以下内容使用递归执行深度优先搜索:

sub build_paths {
   my ($graph) = @_;

   my @paths;

   local *_helper = sub {
      my $v = $_[-1];
      my @successors = $graph->successors($v);
      if (@successors) {
         _helper(@_, $_)
            for @successors;
      } else {
         push @paths, [ @_ ];
      }
   };

   _helper($_)
      for $graph->source_vertices();

   return \@paths;
}

die if $graph->has_a_cycle;

my $paths = build_paths($graph);

是的,可以将作品并行化,但我不是为你写的。

最让我担心的是记忆。根据路径中的分支数量,您可能很容易因内存不足而停止运行。请注意,将路径存储为字符串(逗号分隔值)将比将它们存储为数组所需的内存更少。