C ++ - 如何增加堆栈大小以允许更多的递归,以便Kosaraju的算法计算强连通组件

时间:2016-10-16 10:29:51

标签: c++ algorithm recursion

我使用的是mac,4GB的RAM和CLion IDE。编译器是Clang。我需要在Depth First Search的递归实现中允许更多的递归(目前在具有80k节点的图形上失败)。

typedef unordered_map <int, vector<int>> graph;
void DFS (graph &G, int i, vector <bool> &visited) {

    visited[i] = true;
    for (int j = 0; i < G[i].size(); j++) {
        if (!visited[G[i][j]]) {
            DFS(G, G[i][j], visited);
        }
    }
    t++;
    finishingTime[t] = i; //important step
}

这是为了实现Kosaraju算法来计算图中强连通分量。 https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm我知道可以将DFS实现为迭代,但最后一步很重要,我无法找到使用迭代包含它的方法。这是因为当DFS失败并发生回溯时,该步骤就完成了,递归提供了一种非常自然的方法。

所以目前我只有两个选择:

  • 增加堆栈大小以允许更多递归
  • 或找到迭代解决方案

任何想法如何做?

4 个答案:

答案 0 :(得分:3)

根据注释的建议,您可以将每次调用DFS放在堆栈上分配的堆栈上,然后在DFS的参数列表中进行迭代。堆栈中的每个条目本质上都是一项任务。

伪类代码:

Start and run "recursion":
nr_of_recursions = 0;
dfs_task_stack.push(first_task_params)
while dfs_task_stack not empty
  DFS(dfs_task_stack.pop)
  nr_of_recursions += 1
end while;
true_finishingtime[] = nr_of_recursions - finishingtime[];

DFS:
for each recursion found
  dfs_task_stack.push(task_params)
end for;
t++; finishingtime...

不确定你的算法,但是将你的任务推送到堆栈的顺序可能很重要,即每个......&#34;的顺序。

我冒昧地重新定义了&#34;整理时间&#34;反之。要获得原始定义,请使用递归总数减去新的终结时间。

答案 1 :(得分:0)

我不知道这是否是最佳解决方案,但您可以使用堆栈和访问状态数组,通过拥有多个访问状态来构建完成时间列表。

以下代码仅用于说明算法。我实际上并没有对它进行过多次测试(只是一个小{{0, {1}}, {1, <>}, {2, <>}}小测试)但是我已经在过去以相同的方式使用这种技术来获得更大的图形并且我知道它有效。

我们的想法是在被访问的节点被访问之后将其保持在堆栈中,直到所有被访问的节点被弹出为止,从而模拟递归调用,但推送的数据较少,并且在堆栈对象中。

#include <iostream>
#include <vector>
#include <stack>
#include <cassert>
#include <unordered_map>

using namespace std;

typedef enum {
    vssClean,
    vssPushed,
    vssVisited
} VerticeStackState;


typedef unordered_map <int, vector<int>> graph;
void kosarajuBuildFinishOrder(const int inital, graph &G, vector<int> &finish, vector<VerticeStackState> &state, int &lastFinished) {

    assert(vssClean == state[inital]);



    std::stack<int> stack;

    stack.push(inital);
    state[inital] = vssPushed;

    int current;
    while (!stack.empty())
    {
        current = stack.top();
        if (vssPushed == state[current])
        {
            state[current] = vssVisited;
            for (const auto to: G[current])
            {
                if (state[to]==vssClean)
                {
                    state[to] = vssPushed;
                    stack.push(to);
                }
            }
        }
        else {
            assert(vssVisited == state[current]);
            stack.pop();
            finish[--lastFinished] = current;
        }
    }
}


int main() {
    graph G;
    G.insert({0, vector<int>(1, 1)});
    G.insert({1, vector<int>()});
    G.insert({2, vector<int>()});

    vector<int> finish(G.size(), 0);
    vector <VerticeStackState> state(G.size(), vssClean);


    int  lastFinished = G.size();

    for (int i=0; i < G.size(); ++i) {
        if (vssClean == state[i]){
            kosarajuBuildFinishOrder(i, G, finish, state, lastFinished);
        }
    }

    for (auto i: finish) {
        cout << i << " ";
    }

    return 0;
}

答案 2 :(得分:0)

对于您提到的增加堆栈大小的选项之一,您可以这样做:

g++ -Wl,--stack,16777216 -o kosaraju.exe kosaraju_stl.cpp

这会将堆栈大小增加到16MiB。正如前面的答案中提到的那样,这只是推迟了问题。

答案 3 :(得分:-1)

typedef unordered_map <int, vector<int>> graph;
void DFS (graph &G, vector <bool> &visited) {

std::stack<int> stack;
stack.push(0);  // root 
int i, j;

while(!stack.empty())
{
    i = stack.pop_back();
    visited[i] = true;
    for (j= (int) G[i].size() -1; j >= 0; j--) 
    {
        if (!visited[G[i][j]]) 
        {
            stack.push_back(G[i][j]);

        }
    }
    t++;
    finishingTime[t] = i; //important step
  } // end while.
 }

任何人都可能出现编程错误,因为我没有测试数据,我无法对此进行测试,但输出相同?