图算法:来自邻接图的可达性

时间:2011-09-01 20:18:30

标签: algorithm graph-algorithm

我有一个依赖图,我将其表示为Map<Node, Collection<Node>>(在Java语言中,或f(Node n) -> Collection[Node]作为函数;这是从给定节点n到a的映射依赖n)的节点集合。该图可能是循环*。

给定节点列表badlist,我想解决reachability problem:即生成Map<Node, Set<Node>> badmap,表示列表badlist中每个节点N的映射到一组节点,包括N或其他传递依赖它的节点。

示例:

(x -> y means node y depends on node x)
n1 -> n2
n2 -> n3
n3 -> n1
n3 -> n5
n4 -> n2
n4 -> n5
n6 -> n1
n7 -> n1

这可以表示为邻接地图{n1: [n2], n2: [n3], n3: [n1, n5], n4: [n2, n5], n6: [n1], n7: [n1]}

如果badlist = [n4, n5, n1],我希望得到badmap = {n4: [n4, n2, n3, n1, n5], n5: [n5], n1: [n1, n2, n3, n5]}

我正在寻找在线查找图形算法参考的问题,所以如果有人能指出我对可达性的有效算法描述,我会很感激。 (对我有帮助的一个例子是http://www.cs.fit.edu/~wds/classes/cse5081/reach/reach.html,因为该算法用于确定特定节点A是否可以从特定节点B到达。)

*循环:如果你很好奇,那是因为它代表C / C ++类型,而结构可以有成员指向有问题的结构。

6 个答案:

答案 0 :(得分:3)

在Python中:

def reachable(graph, badlist):
    badmap = {}
    for root in badlist:
        stack = [root]
        visited = set()
        while stack:
            v = stack.pop()
            if v in visited: continue
            stack.extend(graph[v])
            visited.add(v)
        badmap[root] = visited
    return badmap

答案 1 :(得分:2)

这是我最终使用的内容,基于@ quaint的回答:

(为方便起见,需要一些番石榴类)

static public <T> Set<T> findDependencies(
        T rootNode, 
        Multimap<T, T> dependencyGraph)
{
    Set<T> dependencies = Sets.newHashSet();
    LinkedList<T> todo = Lists.newLinkedList();
    for (T node = rootNode; node != null; node = todo.poll())
    {
        if (dependencies.contains(node))
            continue;
        dependencies.add(node);
        Collection<T> directDependencies = 
                dependencyGraph.get(node);
        if (directDependencies != null)
        todo.addAll(directDependencies);
    }
    return dependencies;
}
static public <T> Multimap<T,T> findDependencies(
        Iterable<T> rootNodes, 
        Multimap<T, T> dependencyGraph)
{
    Multimap<T, T> dependencies = HashMultimap.create();
    for (T rootNode : rootNodes)
        dependencies.putAll(rootNode, 
                findDependencies(rootNode, dependencyGraph));
    return dependencies;
}
static public void testDependencyFinder()
{
    Multimap<Integer, Integer> dependencyGraph = 
            HashMultimap.create();
    dependencyGraph.put(1, 2);
    dependencyGraph.put(2, 3);
    dependencyGraph.put(3, 1);
    dependencyGraph.put(3, 5);
    dependencyGraph.put(4, 2);
    dependencyGraph.put(4, 5);
    dependencyGraph.put(6, 1);
    dependencyGraph.put(7, 1);
    Multimap<Integer, Integer> dependencies = 
            findDependencies(ImmutableList.of(4, 5, 1), dependencyGraph);
    System.out.println(dependencies);
    // prints {1=[1, 2, 3, 5], 4=[1, 2, 3, 4, 5], 5=[5]}
}

答案 2 :(得分:2)

您可能应该从邻接列表中构建可达性矩阵以进行快速搜索。我刚刚找到了论文Course Notes for CS336: Graph Theory - Jayadev Misra ,它描述了如何从邻接矩阵构建可达性矩阵。

如果A是您的邻接矩阵,则可达性矩阵将为R = A + A² + ... + A^n,其中n是图中的节点数。 A², A³, ...可以通过以下方式计算:

  • A² = A x A
  • A³ = A x A²
  • ...

对于矩阵乘法,使用逻辑或代替+,并使用逻辑和代替x。复杂度为O(n ^ 4)。

答案 3 :(得分:1)

普通的深度优先搜索或广度优先搜索将解决这个问题:为每个坏节点执行一次。

答案 4 :(得分:1)

这是一个有效的Java解决方案:

// build the example graph
Map<Node, Collection<Node>> graph = new HashMap<Node, Collection<Node>>();
graph.put(n1, Arrays.asList(new Node[] {n2}));
graph.put(n2, Arrays.asList(new Node[] {n3}));
graph.put(n3, Arrays.asList(new Node[] {n1, n5}));
graph.put(n4, Arrays.asList(new Node[] {n2, n5}));
graph.put(n5, Arrays.asList(new Node[] {}));
graph.put(n6, Arrays.asList(new Node[] {n1}));
graph.put(n7, Arrays.asList(new Node[] {n1}));

// compute the badmap
Node[] badlist = {n4, n5, n1};
Map<Node, Collection<Node>> badmap = new HashMap<Node, Collection<Node>>();

for(Node bad : badlist) {
    Stack<Node> toExplore = new Stack<Node>();
    toExplore.push(bad);
    Collection<Node> reachable = new HashSet<Node>(toExplore);
    while(toExplore.size() > 0) {
        Node aNode = toExplore.pop();
        for(Node n : graph.get(aNode)) {
            if(! reachable.contains(n)) {
                reachable.add(n);
                toExplore.push(n);
            }
        }
    }

    badmap.put(bad, reachable);
}

System.out.println(badmap);

答案 5 :(得分:0)

就像使用Christian Ammer一样,在执行以下操作时,您需要A邻接矩阵并使用布尔算法,其中I是单位矩阵。

    B = A + I;
    C = B * B;
    while (B != C) {
        B = C;
        C = B * B;
    }
    return B;

此外,标准矩阵乘法(算术和逻辑)均为O(n^3),而不是O(n^2)。但是如果n <= 64,你可以摆脱一个因素n,因为你现在可以在64位机器上并行执行64位。对于较大的图形,64位并行性也很有用,但着色器技术甚至可能更好。

编辑:一个可以与SSE指令并行执行128位,AVX甚至更多。