【问题标题】:Find all chordless cycles in an undirected graph在无向图中查找所有无弦环
【发布时间】:2011-04-30 16:13:04
【问题描述】:

如何在无向图中找到所有chordless cycles

例如,给定图形

0 --- 1
|     | \
|     |  \
4 --- 3 - 2

算法应该返回 1-2-3 和 0-1-3-4,但绝不会返回 0-1-2-3-4。


(注意:[1]这个问题和small cycle finding in a planar graph不一样,因为图不一定是平面的。[2]我看过论文@ 987654323@ 但我不明白他们在做什么:)。 [3] 我试过CYPATH 但程序只给出计数,readme.txt 中的算法 EnumCordlessPath 有严重的拼写错误,C 代码是一团糟。 [4] 我不想找到任意一组fundametal cycles。循环基础可以有和弦。)

【问题讨论】:

  • @aioobe: 没有限制,但那会效率低下。
  • 在付费墙后面发布论文链接并没有什么好处。
  • @Beta:有时确实如此——对论文的引用很有用,+ ACM 并不是一本晦涩难懂的技术期刊。 (虽然我也无权访问它)
  • @Kenny:构建循环基础的方法不止一种吗?这里似乎有些可疑......看起来像是无弦循环形成了基础,但我可能会遗漏一些明显的东西。
  • 这与求图的最小循环基有何不同? (stackoverflow.com/questions/16782898/…)

标签: algorithm language-agnostic graph graph-theory


【解决方案1】:

查找所有循环。

无弦循环的定义是一组点,其中不存在这些点的子循环。所以,一旦你有了所有的循环问题,就是简单地消除确实有一个子循环的循环。

为了提高效率,对于您找到的每个循环,循环遍历所有现有循环并验证它不是另一个循环的子集,反之亦然,如果是,则消除较大的循环。

除此之外,唯一的困难是弄清楚如何编写一个算法来确定一个集合是否是另一个集合的子集。

【讨论】:

  • 这是一个好主意,但我的直觉是它可能会受到大图的组合爆炸的影响。查看完整图中的循环数:mathworld.wolfram.com/CompleteGraph.html。对于完整图 K16,循环数为 1904975488436,但无弦循环数为 3 顶点循环数 = (16*15*14)/(3*2*1) = 560。
  • 这是不可避免的。我手头没有证据,但我几乎可以保证你必须遍历它们才能找到所有无弦循环。但是,与其在找到所有组合后让每个循环检查所有其他循环,不如在获得它们时消除它们,这意味着您减少了第二遍的内存和工作量。换句话说,图 K16 的时间将是 O(1904975488436 * 560) 而不是 O(1904975488436^2)。
  • “但我几乎可以保证,在找到所有无弦循环之前,你必须遍历它们。”我不同意;如果您正在对图进行广度优先遍历,并且发现了一个无弦循环,则会立即消除很大一部分搜索空间。
  • 你意识到这有点像说,如果你找到了一个半体面的旅行商问题的解决方案,那么你可能离最佳解决方案不远,因此你消除了很大一部分搜索空间通过查看类似的解决方案。不幸的是,您必须检查所有可能性,即图中所有点的幂集。
【解决方案2】:

只是一个想法:

假设您正在枚举示例图上的循环,并且您从节点 0 开始。

如果您对每个给定边进行广度优先搜索,例如0 - 1,你在 1 处到达一个叉子。然后再次到达 0 的循环是无弦的,其余的不是并且可以消除......至少我认为是这样的。

你能用这样的方法吗?或者有反例吗?

【讨论】:

    【解决方案3】:

    这个怎么样。首先,将问题简化为查找通过给定顶点 A 的所有无弦循环。一旦找到所有这些,您就可以从图中删除 A,然后重复另一个点,直到没有任何剩余为止。

    以及如何找到通过顶点 A 的所有无弦循环?将其简化为查找从 B 到 A 的所有无弦路径,给定允许的顶点列表,然后搜索广度优先或深度优先。请注意,当迭代从 B 可到达的顶点(一步)时,当您选择其中一个时,您必须从允许的顶点列表中删除所有其他顶点(当 B=A 时要特别小心,以免消除三个-边缘路径)。

    【讨论】:

      【解决方案4】:

      为从 1 到 n 的节点分配数字。

      1. 选择节点号 1. 称它为“A”。

      2. 枚举来自“A”的链接对。

      3. 选择一个。让我们将 B 小于 C 的相邻节点称为“B”和“C”。

      4. 如果 B 和 C 连接,则输出循环 ABC,返回步骤 3 并选择不同的对。

      5. 如果 B 和 C 没有连接:

        • 枚举连接到 B 的所有节点。假设它连接到 D、E 和 F。创建向量 CABD、CABE、CABF 的列表。对于其中的每一项:
        • 如果最后一个节点连接到除 C 和 B 之外的任何内部节点,则丢弃该向量
        • 如果最后一个节点连接到C,输出并丢弃
        • 如果两者均未连接,则创建一个新的向量列表,并附加最后一个节点所连接的所有节点。

        重复直到用完向量。

      6. 对所有对重复步骤 3-5。

      7. 删除节点 1 和通向它的所有链接。选择下一个节点并返回到第 2 步。

      编辑:您可以取消一个嵌套循环。

      乍一看似乎可行,可能存在错误,但您应该明白:

      void chordless_cycles(int* adjacency, int dim)
      {
          for(int i=0; i<dim-2; i++)
          {
              for(int j=i+1; j<dim-1; j++)
              {
                  if(!adjacency[i+j*dim])
                      continue;
                  list<vector<int> > candidates;
                  for(int k=j+1; k<dim; k++)
                  {
                      if(!adjacency[i+k*dim])
                          continue;
                      if(adjacency[j+k*dim])
                      {
                          cout << i+1 << " " << j+1 << " " << k+1 << endl;
                          continue;
                      }
                      vector<int> v;
                      v.resize(3);
                      v[0]=j;
                      v[1]=i;
                      v[2]=k;
                      candidates.push_back(v);
                  }
                  while(!candidates.empty())
                  {
                      vector<int> v = candidates.front();
                      candidates.pop_front();
                      int k = v.back();
                      for(int m=i+1; m<dim; m++)
                      {
                          if(find(v.begin(), v.end(), m) != v.end())
                              continue;
                          if(!adjacency[m+k*dim])
                              continue;
                          bool chord = false;
                          int n;
                          for(n=1; n<v.size()-1; n++)
                              if(adjacency[m+v[n]*dim])
                                  chord = true;
                          if(chord)
                              continue;
                          if(adjacency[m+j*dim])
                          {
                              for(n=0; n<v.size(); n++)
                                  cout<<v[n]+1<<" ";
                              cout<<m+1<<endl;
                              continue;
                          }
                          vector<int> w = v;
                          w.push_back(m);
                          candidates.push_back(w);
                      }
                  }
              }
          }
      }
      

      【讨论】:

      • 恢复老歌 :) “如果最后一个节点连接到除 C 和 B 之外的任何内部节点,则丢弃向量”中的内部节点是什么意思?不幸的是,我无法真正阅读您的代码。它是内部节点的纯粹定义,即一个有孩子的,还是你的意思是“向量中的一个节点”?我关注的最后一个,但如果它只是“一个内部节点” - 如果形状不是三角形,它如何连接?
      【解决方案5】:

      @aioobe 有道理。只需找到所有循环,然后排除非无弦循环。这可能效率太低,但可以一路修剪搜索空间以减少效率低下。这是一个通用算法:

      void printChordlessCycles( ChordlessCycle path) {
      
        System.out.println( path.toString() );
        for( Node n : path.lastNode().neighbors() ) {
      
          if( path.canAdd( n) ) {
      
            path.add( n);
            printChordlessCycles( path);
            path.remove( n);
          }
        }
      }
      
      Graph g = loadGraph(...);
      ChordlessCycle p = new ChordlessCycle();
      for( Node n : g.getNodes()) {
        p.add(n);
        printChordlessCycles( p);
        p.remove( n);
      }
      
      class ChordlessCycle {
         private CountedSet<Node> connected_nodes;
         private List<Node> path;
      
         ...
      
         public void add( Node n) {
           for( Node neighbor : n.getNeighbors() ) {
             connected_nodes.increment( neighbor);
           }
           path.add( n);
         }
      
         public void remove( Node n) {
           for( Node neighbor : n.getNeighbors() ) {
             connected_nodes.decrement( neighbor);
           }
           path.remove( n);
         }
      
         public boolean canAdd( Node n) {
           return (connected_nodes.getCount( n) == 0);
         }
      }
      

      【讨论】:

        猜你喜欢
        • 2014-01-02
        • 2022-01-21
        • 2018-05-15
        • 1970-01-01
        • 2010-10-07
        • 1970-01-01
        • 2018-04-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多