【问题标题】:Fast algorithm for generating Directed Acyclic Graph生成有向无环图的快速算法
【发布时间】:2015-08-16 02:16:05
【问题描述】:

我有一个问题,非常感谢您的帮助。 我必须生成一个具有 2^N 个节点的 DAG,这些节点的值从 0 到 2^(N-1),具有以下属性: 如果 x

#include <iostream>
#include <vector>
#include <math.h>
typedef unsigned int unint;
using namespace std;
class Node
{
    friend class DAG;
private:
    unint value;
    vector<Node* > neighbourTo;
    vector<Node* > neighbors;
public:
    Node(unint );
};
Node::Node(unint _value)
    : value(_value) {}
class DAG
{
private:
    int noNodes;
    vector<Node* > nodes;
public:
    DAG(int );
    void initializeNodes(int ,int );
    int isPowerOf2(unsigned int );
    int getMaxNaighbourTo(int );
    int getMinNeighbor(int );
    int numberOfPathsLengthK(int );
    int recursion(Node& , int );
    void print();
};
DAG::DAG(int size)
{
    noNodes = size;

    nodes.resize(noNodes);
    int i, j;

    initializeNodes(0, noNodes-1);
    for(i = 0; i < noNodes-1; i++)
    {
        for(j = i+1; j < noNodes; j++)
        {
            if(isPowerOf2(i ^ j))
            {
                nodes[i]->neighbors.push_back(nodes[j]);
                nodes[j]->neighbourTo.push_back(nodes[i]);
            }
        }
    }
}
void DAG::initializeNodes(int min, int max)
{
    if(max == min)
        nodes[max] = new Node(max);
    else
    {
        int s = (max + min)/2;
        initializeNodes(min, s);
        initializeNodes(s+1, max);
    }
}
int DAG::isPowerOf2(unsigned int value)
{
    return ((value != 0) && !(value & (value - 1)));
}
int DAG::getMaxNaighbourTo(int index)
{
    if(index > 0 && index <= (noNodes-1))
    {
        int size = nodes[index]->neighbourTo.size();
        return nodes[index]->neighbourTo[size-1]->value;
    }
    return -1;
}
int DAG::getMinNeighbor(int index)
{
    if(index >= 0 && index < (noNodes-1))
        return nodes[index]->neighbors[0]->value;
    return -1;
}
int DAG::numberOfPathsLengthK(int K)
{
    if(K <= 0)
        return 0;
    long int paths = 0;
    for(int i = 0; i < nodes.size(); i++)
    {
        paths += recursion(*nodes[i], K - 1);
    }
    return (paths % 100003);
}
int DAG::recursion(Node& node, int K)
{
    if( K <= 0 )
        return node.neighbors.size();
    else
    {
        long int paths = 0;
        for(int i = 0; i < node.neighbors.size(); i++)
        {
            paths += recursion(*node.neighbors[i], K - 1);
        }
        return paths;
    }
}
void DAG::print()
{
    for(int i = 0; i < nodes.size(); i++)
    {
        cout << "Node: " << nodes[i]->value << "\tNeighbors: ";
        for(int j = 0; j < nodes[i]->neighbors.size(); j++)
        {
            cout << nodes[i]->neighbors[j]->value << " ";
        }
        cout << endl;
    }
}
int main()
{
    int
    N, M, K,
    i, j;
    cin >> N >> M >> K;
    DAG graf(pow(2, N));
    graf.print();
    cout << "==1==" << endl;
    cout << graf.getMaxNaighbourTo(M) << endl;
    cout << "==2==" << endl;
    cout << graf.getMinNeighbor(M) << endl;
    cout << "==3==" << endl;
    cout << graf.numberOfPathsLengthK(K) << endl;
    return 0;
}

这是一个简单的输出:

4 3 2
Node: 0     Neighbors: 1 2 4 8
Node: 1     Neighbors: 3 5 9
Node: 2     Neighbors: 3 6 10
Node: 3     Neighbors: 7 11
Node: 4     Neighbors: 5 6 12
Node: 5     Neighbors: 7 13
Node: 6     Neighbors: 7 14
Node: 7     Neighbors: 15
Node: 8     Neighbors: 9 10 12
Node: 9     Neighbors: 11 13
Node: 10    Neighbors: 11 14
Node: 11    Neighbors: 15
Node: 12    Neighbors: 13 14
Node: 13    Neighbors: 15
Node: 14    Neighbors: 15
Node: 15    Neighbors:
2
7
48

nodes 是 Node 指针的向量,Node a 是保存节点值和两个向量的类,一个 Node 指向当前节点的邻居,另一个是指向当前节点所在节点的 Node 指针是邻居。上面的代码是用 C++ 编写的。 对于任何语法错误,我深表歉意。英语不是我的母语。

【问题讨论】:

  • 与其生成j 并测试它们是否与i 相差1 位,不如直接从i 开始,只生成与它相差一位的数字?
  • 所有的类方法和属性都是我的母语。翻译需要花点时间。我应该这样做吗?
  • @harold 在初始化当前 i 的邻居向量时,您的意思是遍历与 i 相差一位的数字吗?
  • 如果我正确理解了这个问题,您可以将 2,4,8,16,32... 添加到 i 而不是测试。
  • 是的。请注意,这与 anselm 建议的添加 2 的幂不同,您必须使用 2 的幂进行异或。

标签: c++ optimization directed-acyclic-graphs


【解决方案1】:

第一个明显的非算法性能提升将是来构建图形,如果您只需要打印邻居,您可以这样做而无需创建数据结构。这里的第二个改进是避免用每个输出行刷新流...

对于算法改进,给定一个数字N=0011010(例如,任何数字都有效),您需要找出满足这两个要求的数字,N xor M 是 2 的幂,N &gt; M。第一个要求意味着这两个数字在一个位上完全不同,第二个要求意味着该位必须在M 中亮起,而不是在N 中亮起,所以只看上面的位的答案是:@987654326 @。现在您可以通过扫描N 中的每个位来获取所有这些,如果是0,则设置并打印值。

// assert that 'bits < CHAR_BITS * sizeof(unsigned)'
const unsigned int max = 1u << bits;
for (unsigned int n = 1; n < max; ++n) {
   std::cout << "Node: " << n << " Neighbors: ";
   for (unsigned int bit = 0; i < bits; ++i) {
      unsigned int mask = 1 << bit;
      if (!(n & mask)) {
         std::cout << (n | mask);
      }
   }
   std::cout << '\n';
}

对于给定节点的最小和最大邻居,您可以应用相同的推理,给定数量 N 的最大可达邻居将是 M,使得 N 中的最高 0 位被点亮。对于最小可达邻居,您需要 M 以便将最低 0 位设置为 1。

【讨论】:

  • 大卫,我需要数据进行额外分析,这就是我存储所有数据的原因。关于您的解决方案建议,这就是我在论坛上少数人的帮助下最后所做的。不过,感谢您抽出宝贵时间。
  • @user242760:对 malloc 的一次调用值得进行一些位操作。对于您正在执行的任何处理,尝试根据需要生成邻居可能是值得的。例如,从一个节点可达的邻居数是位模式中 0 的个数,最大可达邻居是将最高的 0 设置为 1,最小的将最低的 1 设置为 0...
【解决方案2】:

我有空闲时间写一个草图,看看:

struct node
{
    std::vector<std::shared_ptr<node> > link;
};

int main()
{
    int N = 4;

    int M = 1<<N;
    std::vector<std::shared_ptr<node> > tree(M, std::make_shared<node>());

    for(int i=0;i<M;++i)
    {
        std::cout<<"node: "<<i<<" is connected to:\n";

        for(int p=0;p<N;++p)
        {
            int j= (1<<p) ^ i;    //this is the evaluation you asked for
                                  //it's the inverse of i ^ j = 2^p

            if(j<=i) continue;                
            tree[i]->link.push_back(tree[j]);

            std::cout<<j<<"  ";
        }
        std::cout<<std::endl;        
    }
}

DEMO

对于N=4,即2^4=16 节点,程序打印

node: 0 is connected to:
1  2  4  8  
node: 1 is connected to:
3  5  9  
node: 2 is connected to:
3  6  10  
node: 3 is connected to:
7  11  
node: 4 is connected to:
5  6  12  
node: 5 is connected to:
7  13  
node: 6 is connected to:
7  14  
node: 7 is connected to:
15  
node: 8 is connected to:
9  10  12  
node: 9 is connected to:
11  13  
node: 10 is connected to:
11  14  
node: 11 is connected to:
15  
node: 12 is connected to:
13  14  
node: 13 is connected to:
15  
node: 14 is connected to:
15  
node: 15 is connected to:

我希望这就是您想要的。玩得开心。

【讨论】:

  • 我看到您在我回答时进行了编辑。请看一下结果是否正确,否则我可以细化。
  • 你能告诉我你的算法的时间复杂度吗?
  • @user242760:复杂度为N * 2^N,但代码错误......我将您的异或符号解释为“加号”。我会努力解决的。
  • @user242760:在this thread 的帮助下修复了它。复杂度仍然是O(N * 2^N)(而不是蛮力方法的O(2 ^ 2N)
  • davidhigh,您能否提出一种更快的算法来查找生成图中长度为 K 的路径的数量?我尝试了递归算法,但它比我预期的要慢。代码在上面。提前谢谢你
猜你喜欢
  • 2011-07-30
  • 1970-01-01
  • 1970-01-01
  • 2012-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-15
  • 1970-01-01
相关资源
最近更新 更多