Algorithm 对于有向图,最著名的传递闭包算法是什么?

Algorithm 对于有向图,最著名的传递闭包算法是什么?,algorithm,graph,closures,Algorithm,Graph,Closures,在运行时方面,最著名的有向图传递闭包算法是什么 我目前正在使用Warshall的算法,但它是O(n^3)。尽管如此,由于图形表示,我的实现做得稍微好一点(而不是检查所有边,它只检查所有向外的边)。有没有比这更好的传递闭包算法?特别是,是否有专门针对共享内存多线程体系结构的内容?提供了一些有用的信息。要点: 传递闭包和矩阵乘法一样困难;因此,最著名的界是运行在O(n^2.376)中的界,但在实践中,可能不值得使用矩阵乘法算法 对于启发式加速,首先计算强连接组件 本文讨论了各种传递闭包算法的性能:

在运行时方面,最著名的有向图传递闭包算法是什么

我目前正在使用Warshall的算法,但它是O(n^3)。尽管如此,由于图形表示,我的实现做得稍微好一点(而不是检查所有边,它只检查所有向外的边)。有没有比这更好的传递闭包算法?特别是,是否有专门针对共享内存多线程体系结构的内容?

提供了一些有用的信息。要点:

  • 传递闭包和矩阵乘法一样困难;因此,最著名的界是运行在O(n^2.376)中的界,但在实践中,可能不值得使用矩阵乘法算法
  • 对于启发式加速,首先计算强连接组件

本文讨论了各种传递闭包算法的性能:

本文中一个有趣的想法是避免在图形更改时重新计算整个闭包

还有Esko Nuutila的这一页,其中列出了一些较新的算法:

该页上列出的博士论文可能是最好的起点:

从该页:

实验还表明,在区间表示法下 新算法可以计算传递闭包 通常在时间上与输入图形的大小呈线性关系


令人惊讶的是,我找不到
STACK\u TC
算法的任何实现(在另一个答案中由AmigoNico链接)

<>我在C++中编写了自己的简单实现。它与原始算法略有不同,请参见代码中的注释以了解解释

它成功地通过了我尝试过的一些测试,但鼓励读者进行更多的测试,并对照原始论文进行验证。代码可能没有得到充分优化

struct TransitiveClosure
{
//图的节点被分组为“组件”。
//在组件中,每个节点都可以从其他节点访问(可能是间接的)。
//如果组件数与节点数相同,则图形没有循环。
//否则,组件将减少。
//这些组件形成一个称为“凝聚图”的图,该图始终是非循环的。
//组件的编号方式为“B可从a访问”意味着“B在后续循环中”,原始算法堆栈检查
//>边是所谓的前向边。我们不执行此检查,这可能导致
//>一个组件被多次推送到cstack。因为在
//>拓扑排序,这些将在以后删除,不会导致正确性问题。
}
}
if(ret.nodes[v].root==v)
{
std::size_t c=ret.components.size();
ret.components.emplace_back();
TransitiveClosure::Component&this_comp=ret.components.back();
此组件下一个包含.assign(ret.components.size(),false);//Sic。
if(vstack.back()!=v | | self|u循环)
{
此组件下一个。推回(c);
此组件下一个包含[c]=true;
}
//对组件堆栈的一部分进行拓扑排序。
标准::排序(cstack.begin()+保存的高度,cstack.end(),[&comp=ret.components](标准::大小a,标准::大小b)->bool
{
如果(b>=comp[a],则下一步包含.size()
返回false;
返回组件[a]。下一个包含[b];
});
//删除重复项。
擦除(std::unique(cstack.begin()+保存的高度,cstack.end()),cstack.end());
while(cstack.size()!=保存的高度)
{
std::size_t x=cstack.back();
cstack.pop_back();
如果(!此组件下一个包含[x])
{
如果(!此组件下一个包含[x])
{
此组件下一个。推回(x);
此组件下一个包含[x]=true;
}
此组件next.reserve(此组件next.size()+ret.components[x].next.size());
对于(std::size\u t c:ret.components[x]。下一步)
{
如果(!此组件下一个包含[c])
{
此组件下一个。推回(c);
此组件下一个包含[c]=true;
}
}
}
}
标准:尺寸(w);
做
{
w=vstack.back();
vstack.pop_back();
ret.nodes[w].comp=c;
此组件节点向后推(w);
}
而(w!=v);
}
};
对于(std::size\u t v=0;v
我的测试用例(来自同一篇论文):

  • 输入:(邻接矩阵,Y为边源,X为边目标)

    {0,1,0,0,0,0,0},
    {0,0,1,1,0,0,0,0},
    {1,0,0,1,0,0,0,0},
    {0,0,0,0,1,1,0,0},
    {0,0,0,0,0,0,1,0},
    {0,0,0,0,1,0,0,1},
    {0,0,0,0,1,0,0,0},
    {0,0,0,0,0,1,0,0},
    
    输出:

    {nodes=[(0,3)、(0,3)、(0,3)、(3,2)、(4,0)、(5,1)、(4,0)、(5,1)],components=[{nodes=[6,4],next=[0],next_contains=[1]},{nodes=[7,5],next=[1,0],next_contains=[1,1]},{nodes
    
  • 输入:

    //a b c d e f g h i j
    /*a*/{0,1,0,0,0,1,0,1,0,0},
    /*b*/{1,0,1,0,0,0,0,0},
    /*c*/{0,1,0,1,0,0,0,0,0},
    /*d*/{0,0,0,0,1,0,0,0,0,0},
    /*e*/{0,0,0,1,0,0,0,0,0},
    /*f*/{0,0,0,0,0,0,1,0,0,0},
    /*g*/{0,0,0,1,0,1,0,0,0},
    /*h*/{0,0,0,