【问题标题】:Graph algorithm - Looking to improve scalability图算法 - 寻求提高可扩展性
【发布时间】:2009-09-07 09:39:12
【问题描述】:

我编写了一个算法来计算和存储 DAG 的所有路径,它在小图上运行良好 - 但现在我正在寻求提高它在更大图上运行的效率。该算法的核心逻辑在下面的 createSF() 和 makePathList() 中,其他方法是 helpers - 我可以看到 append 是一个瓶颈。但是,我想最大的帮助是设计一个可以将路径存储在字典中的数据结构,因为许多路径是由其他路径组成的,这是我问题的症结所在。

private Multiset<String> paths = new Multiset<String>();    

public Multiset<String> createSF(DAGNode n) {

    for (DAGNode succ : n.getSuccessors())
        createSF(succ);
    if (!n.isVisited())
        for (String s : makePathList(n)) 
            paths.put(s);

    n.setVisited(true);
    return paths;
}

private List<String> makePathList(DAGNode n) {
    List<String> list = new ArrayList<String>();

    list.add(n.getLabel());
    for (DAGNode node : n.getSuccessors())
        list.addAll(append(n.getLabel(), makePathList(node)));

return list;
}

private List<String> append(String s, List<String> src) {
    List<String> ls = new ArrayList<String>();
    for (String str : src) 
    ls.add(s + "/" + str);

    return ls;
}

编辑:

我现在使用一个路径对象来表示路径,并指出了这两种方法的瓶颈:

public List<Path> createPathList(Tree n) {
    List<Path> list = new ArrayList<Path>();
    list.add(new Path(n.getNodeName()));
    for (Tree node : n.getSuccessors()) {
        list.addAll(append(n.getNodeName(), createPathList(node)));
    }
    return list;
}

public List<Path> append(String s, List<Path> src) {
    List<Path> ls = new ArrayList<Path>();
    for (Path path : src) {
        ls.add(new Path(path, s));
    }
    return ls;
}

问题是当一个图的大小为 M 时,这些方法将被调用 M 次,这意味着这里创建了很多列表...有没有更有效的方法来建立 createPathList() 的返回?

【问题讨论】:

  • 你希望用你的路径做什么?为什么将它们中的每一个都存储为一个列表?

标签: java algorithm optimization scalability graph


【解决方案1】:

为了回答这个问题,有必要了解为什么需要路径列表。路径列表不会为您提供有关 DAG 表示形式的任何其他信息。

如果您想分别计算每条路径的值,或者计算所有路径的总和/最小值/最大值,也可以使用 DAG 本身来完成。

如果您确实坚持保存单独的路径,一种选择是将您的 DAG 转换为 Trie 的变体。另一种选择可能是使用Lempel-Ziv 表示的某些变体。这取决于您的 DAG 类型,以及您希望如何处理路径信息。

【讨论】:

  • 我特别需要以多组路径的形式使用它,因为我以这种形式将它用于确定熵复杂度的另一种算法。
  • 在这种情况下,将路径存储在不同的数据结构中对您没有帮助,因为您需要全长字符串表示。
  • 编辑:如果您可以更改第二种算法的参数,Lempel-Ziv(字典)样式表示可能会节省一些空间,并且工作得更快。
【解决方案2】:

看看Graphviz的DOT源代码,可能会给你一些想法。

【讨论】:

    【解决方案3】:

    请允许我先放两个(希望有帮助)cmets:

    我在理解您的代码时遇到了一些困难,因为某些方法名称误导了我。从名字看,我期待着别的东西。我可以建议一些重构:

    makePathList -> createPathList  // you actually create a List here
    append -> createPathList // yes, same name as above because it creates the same
                             // type of list, just with different parameters
    

    删除了罗伯特编辑后变得过时的部分答案

    正如 Margus 所说,用 StringBuilder 附加链替换 String 连接不会提高性能。编译器可能会优化 String 连接到 StringBuilder 附加(我见过这样的字节码)。

    您可以尝试将 DAG 转换为树结构。引入一个不可见的根,所有节点都作为直接子节点。现在为每个节点添加它的后继者(子节点和/或兄弟节点)。现在叶子的数量应该等于路径的数量,并且从根到任何叶子的每个图都是 DAG 中的一条路径。

    编辑

    一个小的改进——它是微优化,但至少它会留下更少的垃圾:

    private List<String> append(String node, List<String> allPathsStartingAfterNode) {
        List<String> allPathsStartingAtNode = new ArrayList<String>();
        String nodeWithSeparator = node + "/";
    
        for (String aPathStartingAfterNode : allPathsStartingAfterNode) {
            allPathsStartingAtNode.add(nodeWithSeparator + aPathStartingAfterNode);
        }
    
        return allPathsStartingAtNode;
    }
    

    【讨论】:

    • 对不起,我使用树作为输入时留下了一些多余的代码
    【解决方案4】:

    一个简单的修改(取决于你如何使用数据)可能是延迟加载路径,这样如果你倾向于只使用一些路径,你甚至永远不会生成一些路径。

    当然,这完全取决于预期用途

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-23
      • 1970-01-01
      • 1970-01-01
      • 2019-02-23
      • 2015-08-18
      • 2014-12-28
      • 2016-01-28
      • 1970-01-01
      相关资源
      最近更新 更多