我有一个节点列表,每个节点都分解为更多节点。例如

  • Node0 = w01 * Node1 + w02 * Node2 + w03 * Node3的
  • Node1 = w12 * Node2 + w14 * Node4

  • 因此,我们有Node0 = w01 * w12 * Node2 + w03 * Node3 + w01 * w14 Node4。

    我的C++代码用于对给定的一组权重分解执行上述聚合/分解/合并,如下所示。但是,我觉得有很多需要优化的地方。仅举一个例子,我正在遍历topWeights的密钥,并将它们收集在topNodeNames中,这似乎效率很低。

    是否有任何STL算法可以帮助我加快速度,并避免不必要的复制?
    #include <string>
    #include <unordered_map>
    
    template<class T, class U> using umap = std::unordered_map<T, U>;
    
    
    umap<std::string, double> getWeights(const std::string& nodeName, const umap<std::string, umap<std::string, double>>& weightTrees)
    {
        const auto it = weightTrees.find(nodeName);
        if (it == weightTrees.end())
            return umap<std::string, double>();
    
        umap<std::string, double> topWeights = it->second;
        std::vector<std::string> topNodeNames;
    
        for (const auto& kv : topWeights)
            topNodeNames.push_back(kv.first);
    
        for (const std::string& topNodeName : topNodeNames)
        {
            umap<std::string, double> subWeights = getWeights(topNodeName, weightTrees);
            if (subWeights.size() > 0)
            {
                const double topWeight = topWeights[topNodeName];
                topWeights.erase(topNodeName);
                for (const auto& subWeight : subWeights)
                {
                    const auto it = topWeights.find(subWeight.first);
                    if (it == topWeights.end())
                        topWeights[subWeight.first] = topWeight * subWeight.second;
                    else
                        it->second += topWeight * subWeight.second;
                }
            }
        }
    
        return topWeights;
    }
    
    
    int main()
    {
        umap<std::string, umap<std::string, double>> weightTrees = {{ "Node0", {{ "Node1",0.5 },{ "Node2",0.3 },{ "Node3",0.2 }} },
                                                                    { "Node1", {{ "Node2",0.1 },{ "Node4",0.9 }} }};
    
        umap<std::string, double> w = getWeights("Node0", weightTrees); // gives {Node2: 0.35, Node3: 0.20, Node4: 0.45}
    }
    

    最佳答案

    主要问题是,您将每个节点的递归到每个子节点的,这通常是高度冗余的。避免这种情况的一种方法是在节点名称上引入顺序,其中“较高”的节点仅取决于“较低”的节点,然后以相反的顺序进行计算(对于每个节点,您已经完全知道所有子权重)。但是,我认为没有std算法会为您找到此顺序,因为您不能廉价地临时确定节点依赖性(“节点X是否依赖于节点Y?如果不是直接依赖于节点Y,我们可能必须搜索整个树...”)。

    因此,您可以走动态编程路线,并将完全计算出的节点存储在某个位置。甚至更好-您可以在遍历整个树时将其整平为只有叶子的权重。只要您在整个递归中保持展平,实际上在递归形式中这是相当优雅的:

    using NodeWeights = std::unordered_map<std::string, double>;
    using NonLeaves = std::unordered_map<std::string, NodeWeights>;
    
    // Modifies the tree so that the given root has no non-leaf children.
    void flattenTree(std::string root, NonLeaves& toFlatten)
    {
        auto rootIt = toFlatten.find(root);
        if (rootIt == toFlatten.end())
            return;
    
        NodeWeights& rootWeights = rootIt->second;
    
        NodeWeights leafOnlyWeights;
    
        for (auto kvp : rootWeights)
        {
            const std::string& childRoot = kvp.first;
            double childWeight = kvp.second;
    
            std::cout << "Checking child " << childRoot << std::endl;
    
            // If the graph is indeed acyclic, then the root kvp here is untouched
            // by this call (and thus references to it are not invalidated).
            flattenTree(childRoot, toFlatten);
    
            auto childIt = toFlatten.find(childRoot);
    
            // The child is a leaf after flattening: Do not modify anything.
            if (childIt == toFlatten.end())
            {
                leafOnlyWeights[childRoot] = childWeight;
                continue;
            }
    
            // Child is still not a leaf (but all its children are now leaves):
            // Redistribute its weight among our other child weights.
            const NodeWeights& leafWeights = childIt->second;
            for (auto leafKvp : leafWeights)
                leafOnlyWeights[leafKvp.first] += childWeight * leafKvp.second;
        }
    
        rootWeights = leafOnlyWeights;
    }
    
    int main()
    {
        umap<std::string, umap<std::string, double>> weightTrees = {{ "Node0", {{ "Node1",0.5 },{ "Node2",0.3 },{ "Node3",0.2 }} },
                                                                    { "Node1", {{ "Node2",0.1 },{ "Node4",0.9 }} }};
    
        auto flattenedTree = weightTrees;
        flattenTree("Node0", flattenedTree);
    
        umap<std::string, double> w = flattenedTree["Node0"]; // Should give {Node2: 0.35, Node3: 0.20, Node4: 0.45}
    
        for (auto kvp : w)
          std::cout << kvp.first << ": " << kvp.second << std::endl;
    }
    

    Demo

    由于每个节点最多只能展平一次,因此您将无法运行原始算法具有的指数运行时间。

    10-01 20:01