C++ 生成随机DAG

C++ 生成随机DAG,c++,c,algorithm,graph,cycle,C++,C,Algorithm,Graph,Cycle,我正在解决一个关于有向无环图的问题 但是我在一些有向无环图上测试代码时遇到了困难。测试图应该是大的,并且(显然)是非循环的 我试着写了很多代码来生成无环有向图。但我每次都失败了 是否有一些现有方法可用于生成无环有向图?创建一个包含n节点以及每对节点n1和n2ifn1!=n2和n2%n1==0您可以生成一个随机有向图,然后对循环进行深度优先搜索。找到循环后,通过删除边来打断它 我认为这是最坏的情况。每个DFS取O(V),每个DFS至少删除一条边(因此最大E) 如果您通过均匀随机选择所有V^2可能的

我正在解决一个关于有向无环图的问题

但是我在一些有向无环图上测试代码时遇到了困难。测试图应该是大的,并且(显然)是非循环的

我试着写了很多代码来生成无环有向图。但我每次都失败了


是否有一些现有方法可用于生成无环有向图?

创建一个包含
n
节点以及每对节点
n1
n2
if
n1!=n2
n2%n1==0

您可以生成一个随机有向图,然后对循环进行深度优先搜索。找到循环后,通过删除边来打断它

我认为这是最坏的情况。每个DFS取O(V),每个DFS至少删除一条边(因此最大E)

如果您通过均匀随机选择所有V^2可能的边来生成有向图,并且您以随机顺序DFS并删除一条随机边-这将使您在所有可能的DAG上获得均匀分布(或至少接近它)。

答案适用:如果您有图边的邻接矩阵表示,如果矩阵是下三角的,它必然是一个DAG

一种类似的方法是对节点进行任意排序,然后仅在x<y时考虑从节点X到Y的边。这个约束也应该通过构造来获得你的DAGness。如果使用结构来表示节点,那么内存比较将是对节点进行排序的一种任意方式

基本上,伪代码类似于:

for(i = 0; i < N; i++) {
    for (j = i+1; j < N; j++) {
        maybePutAnEdgeBetween(i, j);
    }
}
既然有

n*(n-1)/2

有序对(“N选择2”),我们可以选择它们之间是否有边缘。

我编写了一个C程序来实现这一点。关键是对节点进行“排序”,并且只从排名较低的节点到排名较高的节点绘制边

我写的程序打印出来了

下面是代码本身,注释解释了它的含义:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MIN_PER_RANK 1 /* Nodes/Rank: How 'fat' the DAG should be.  */
#define MAX_PER_RANK 5
#define MIN_RANKS 3    /* Ranks: How 'tall' the DAG should be.  */
#define MAX_RANKS 5
#define PERCENT 30     /* Chance of having an Edge.  */

int main (void)
{
  int i, j, k,nodes = 0;
  srand (time (NULL));

  int ranks = MIN_RANKS
              + (rand () % (MAX_RANKS - MIN_RANKS + 1));

  printf ("digraph {\n");
  for (i = 0; i < ranks; i++)
    {
      /* New nodes of 'higher' rank than all nodes generated till now.  */
      int new_nodes = MIN_PER_RANK
                      + (rand () % (MAX_PER_RANK - MIN_PER_RANK + 1));

      /* Edges from old nodes ('nodes') to new ones ('new_nodes').  */
      for (j = 0; j < nodes; j++)
        for (k = 0; k < new_nodes; k++)
          if ( (rand () % 100) < PERCENT)
            printf ("  %d -> %d;\n", j, k + nodes); /* An Edge.  */

      nodes += new_nodes; /* Accumulate into old node set.  */
    }
  printf ("}\n");
  return 0;
}
#包括
#包括
#包括
#定义MIN_PER_秩1/*节点/秩:DAG的“胖”程度*/
#定义每个等级5的最大值
#定义最小等级3/*等级:DAG的“高度”应为多少*/
#定义最大等级5
#定义具有优势的概率为30/*的百分比*/
内部主(空)
{
int i,j,k,节点=0;
srand(时间(空));
整数秩=最小秩
+(rand()%(最大秩-最小秩+1));
printf(“有向图{\n”);
对于(i=0;i%d;\n”,j,k+节点);/*一条边*/
节点+=新的_节点;/*累积到旧节点集中*/
}
printf(“}\n”);
返回0;
}
下面是测试运行生成的图形:


所以,试着把所有这些合理的答案放在一起:

(在下面,我使用V表示生成的图中的顶点数,E表示边数,我们假设E≤ V(V-1)/2.)

就个人而言,我认为最有用的答案是弗拉维乌斯(Flavius)的评论,他指向。这段代码非常简单,可以通过一条注释方便地描述,我复制了这条注释:

To generate a directed acyclic graph, we first
generate a random permutation dag[0],...,dag[v-1].
(v = number of vertices.)
This random permutation serves as a topological
sort of the graph. We then generate random edges of the
form (dag[i],dag[j]) with i < j.
要生成有向无环图,我们首先
生成一个随机排列dag[0],…,dag[v-1]。
(v=顶点数。)
这种随机排列可用作拓扑结构
有点像图表。然后,我们生成图像的随机边
带i
事实上,代码所做的是通过重复执行以下操作来生成请求数量的边:

  • 生成范围为[0,V]的两个数字
  • 如果他们平等,就拒绝他们
  • 如果第一个较大,则交换它们
  • 如果以前生成过,则拒绝它们
  • 此解决方案的问题是,当E接近最大边数V(V-1)/2时,算法变得越来越慢,因为它必须拒绝越来越多的边。更好的解决方案是将所有V(V-1)/2条可能的边生成一个向量;随机洗牌;然后选择第一条(请求的边)无序列表中的边

    让我们在空间O(E)中这样做,因为我们可以从k的值推断第k条边的端点。因此,我们实际上不必创建源向量。但是,它仍然需要O(V2)时间

    或者,你也可以做一次洗牌(或者Knuth洗牌,如果你愿意的话),在E迭代后停止。在维基百科上展示的FY洗牌版本中,这将产生尾随条目,但算法向后运行也同样有效:

    // At the end of this snippet, a consists of a random sample of the
    // integers in the half-open range [0, V(V-1)/2). (They still need to be
    // converted to pairs of endpoints).
    vector<int> a;
    int N = V * (V - 1) / 2;
    for (int i = 0; i < N; ++i) a.push_back(i);
    for (int i = 0; i < E; ++i) {
      int j = i + rand(N - i);
      swap(a[i], a[j]);
    a.resize(E);
    
    //在这个代码段的末尾,一个由
    //半开范围[0,V(V-1)/2]中的整数。(它们仍然需要
    //转换为端点对)。
    载体a;
    int N=V*(V-1)/2;
    对于(int i=0;i
    这只需要O(E)时间,但需要O(N2)空间。事实上,可以通过一些技巧将其改进为O(E)空间,但SO代码段太小,无法包含结果,因此我将在O(E)空间和O(E log E)时间中提供一个更简单的代码段。我假设有一个类DAG,它至少包含:

    class DAG {
      // Construct an empty DAG with v vertices
      explicit DAG(int v);
    
      // Add the directed edge i->j, where 0 <= i, j < v
      void add(int i, int j);
    };
    
    类DAG{
    //使用v顶点构造空DAG
    显式DAG(int-v);
    
    //添加有向边i->j,其中0我最近尝试重新实现接受的答案,发现它是不确定的。如果不强制执行min_per_rank参数,可能会得到一个节点数为0的图

    为了防止出现这种情况,我将for循环包装在一个函数中,然后检查以确保
    class DAG {
      // Construct an empty DAG with v vertices
      explicit DAG(int v);
    
      // Add the directed edge i->j, where 0 <= i, j < v
      void add(int i, int j);
    };
    
    // Return a randomly-constructed DAG with V vertices and and E edges.
    // It's required that 0 < E < V(V-1)/2.
    template<typename PRNG>
    DAG RandomDAG(int V, int E, PRNG& prng) {
      using dist = std::uniform_int_distribution<int>;
      // Make a random sample of size E
      std::vector<int> sample;
      sample.reserve(E);
      int N = V * (V - 1) / 2;
      dist d(0, N - E);  // uniform_int_distribution is closed range
      // Random vector of integers in [0, N-E]
      for (int i = 0; i < E; ++i) sample.push_back(dist(prng));
      // Sort them, and make them unique
      std::sort(sample.begin(), sample.end());
      for (int i = 1; i < E; ++i) sample[i] += i;
      // Now it's a unique sorted list of integers in [0, N-E+E-1]
      // Randomly shuffle the endpoints, so the topological sort
      // is different, too.
      std::vector<int> endpoints;
      endpoints.reserve(V);
      for (i = 0; i < V; ++i) endpoints.push_back(i);
      std::shuffle(endpoints.begin(), endpoints.end(), prng);
      // Finally, create the dag
      DAG rv;
      for (auto& v : sample) {
        int tail = int(0.5 + sqrt((v + 1) * 2));
        int head = v - tail * (tail - 1) / 2;
        rv.add(head, tail);
      }
      return rv;
    }
    
    int pushed = 0
    
    int addRank (void) 
    {
      for (j = 0; j < nodes; j++)
        for (k = 0; k < new_nodes; k++)
          if ( (rand () % 100) < PERCENT)
            printf ("  %d -> %d;\n", j, k + nodes); /* An Edge.  */
    
      if (pushed < min_per_rank) return addRank()
      else pushed = 0
    
      return 0
    }
    
    import random
    
    class Graph:
        nodes = []
        edges = []
        removed_edges = []
    
        def remove_edge(self, x, y):
            e = (x,y)
            try:
                self.edges.remove(e)
                # print("Removed edge %s" % str(e))
                self.removed_edges.append(e)
            except:
                return
    
        def Nodes(self):
            return self.nodes
    
        # Sample data
        def __init__(self):
            self.nodes = []
            self.edges = []
    
    
    def get_random_dag():
        MIN_PER_RANK = 1    # Nodes/Rank: How 'fat' the DAG should be
        MAX_PER_RANK = 2
        MIN_RANKS = 6   # Ranks: How 'tall' the DAG should be
        MAX_RANKS = 10
        PERCENT = 0.3  # Chance of having an Edge
        nodes = 0
    
        ranks = random.randint(MIN_RANKS, MAX_RANKS)
    
        adjacency = []
        for i in range(ranks):
            # New nodes of 'higher' rank than all nodes generated till now
            new_nodes = random.randint(MIN_PER_RANK, MAX_PER_RANK)
    
            # Edges from old nodes ('nodes') to new ones ('new_nodes')
            for j in range(nodes):
                for k in range(new_nodes):
                    if random.random() < PERCENT:
                        adjacency.append((j, k+nodes))
    
            nodes += new_nodes
    
        # Compute transitive graph
        G = Graph()
        # Append nodes
        for i in range(nodes):
            G.nodes.append(i)
        # Append adjacencies
        for i in range(len(adjacency)):
            G.edges.append(adjacency[i])
    
        N = G.Nodes()
        for x in N:
            for y in N:
                for z in N: 
                    if (x, y) != (y, z) and (x, y) != (x, z):
                        if (x, y) in G.edges and (y, z) in G.edges:
                            G.remove_edge(x, z)
    
        # Print graph
        for i in range(nodes):
            print(i)
        print()
        for value in G.edges:
            print(str(value[0]) + ' ' + str(value[1]))
    
    get_random_dag()
    
    def get_random_dag():
        MIN_PER_RANK = 1    # Nodes/Rank: How 'fat' the DAG should be
        MAX_PER_RANK = 3
        MIN_RANKS = 15   # Ranks: How 'tall' the DAG should be
        MAX_RANKS = 20
        PERCENT = 0.3  # Chance of having an Edge
        nodes = 0
        node_counter = 0
    
        ranks = random.randint(MIN_RANKS, MAX_RANKS)
    
        adjacency = []
        rank_list = []
        for i in range(ranks):
            # New nodes of 'higher' rank than all nodes generated till now
            new_nodes = random.randint(MIN_PER_RANK, MAX_PER_RANK)
    
            list = []
            for j in range(new_nodes):
                list.append(node_counter)
                node_counter += 1
            rank_list.append(list)
    
            print(rank_list)
    
            # Edges from old nodes ('nodes') to new ones ('new_nodes')
            if i > 0:
                for j in rank_list[i - 1]:
                    for k in range(new_nodes):
                        if random.random() < PERCENT:
                            adjacency.append((j, k+nodes))
    
            nodes += new_nodes
    
        for i in range(nodes):
            print(i)
        print()
        for edge in adjacency:
            print(str(edge[0]) + ' ' + str(edge[1]))
        print()
        print()