将递归排列生成器转换为迭代生成器

发布于 2024-11-24 02:30:27 字数 1087 浏览 1 评论 0原文

我在将这种用于显示给定整数集的所有排列的递归算法转换为迭代算法时遇到了一些困难。

void getPermutationsR(int v[], int n, int i) 
{
    if (i == n)
    {
        //Display contents of v
    } 
    else
    {
        for (int j=i; j<n; j++) 
        {
            swap (v, i, j);
            getPermutationsR(v, n, i+1);
            swap (v, i, j);
        }
    }
}

这是我当前的尝试,它是完全错误的,但如果不使用本机迭代算法来解决问题,我看不到任何方法来纠正它。我的一半尝试是“弹出”多于“推送”(当我尝试访问空堆栈中的元素时导致错误),另一半尝试“推送”多于“弹出”(无限循环)。

void getPermutationsI(int v[], int n, int i) 
    {
    stack<int> iStack;
    stack<int> jStack;

    iStack.push(i);
    jStack.push(i);

    while(iStack.size() > 0)
    {
        if (iStack.top() == n)
        {
            jStack.pop();
            iStack.pop();
            //Display contents of v
        }
        else
        {
            for (int j = iStack.top(); j < n; j++)
            {
               //swap 
                               //something to do with jStack
            }
            //swap 
            iStack.push(i+1);
        }
    }
}

I'm having some difficulty converting this recursive algorithm for displaying all the permutations of a given set of integers into an iterative one.

void getPermutationsR(int v[], int n, int i) 
{
    if (i == n)
    {
        //Display contents of v
    } 
    else
    {
        for (int j=i; j<n; j++) 
        {
            swap (v, i, j);
            getPermutationsR(v, n, i+1);
            swap (v, i, j);
        }
    }
}

This is my current attempt, it's completely wrong but I can't see any way to correct it without using a natively iterative algorithm for the problem . Half my attempts have had me 'popping' more than 'pushing' (Leads to an error as I attempt to access elements in an empty stack) and the other half I'm 'pushing' more than 'popping' (infinite loop).

void getPermutationsI(int v[], int n, int i) 
    {
    stack<int> iStack;
    stack<int> jStack;

    iStack.push(i);
    jStack.push(i);

    while(iStack.size() > 0)
    {
        if (iStack.top() == n)
        {
            jStack.pop();
            iStack.pop();
            //Display contents of v
        }
        else
        {
            for (int j = iStack.top(); j < n; j++)
            {
               //swap 
                               //something to do with jStack
            }
            //swap 
            iStack.push(i+1);
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

妥活 2024-12-01 02:30:31

您可以使用STL:

a[]={0,1,2,....n-1};
do{
    for(int i=0;i<n;++i)
      //  print v[ a[i] ]
    //print EOLN
}
while(next_permutation(a,a+n));

未测试。

You can use STL:

a[]={0,1,2,....n-1};
do{
    for(int i=0;i<n;++i)
      //  print v[ a[i] ]
    //print EOLN
}
while(next_permutation(a,a+n));

Not tested.

如梦 2024-12-01 02:30:31

您需要阅读TAOCP的第7.2.1.2章。


编辑:
根据评论中的讨论,基于堆栈的递归消除也很好(尽管在我看来这不是纯粹的迭代方法)。
这是基于堆栈的版本的草稿:

void getPermutationsNR(int v[]) 
{
    struct task
    {
        enum { swap, reccall } tasktype;
        int i, j;
    }
    stack<task> stack;
    stack.push(new task() {tasktype=reccall, i=0}); // initial task
    while (!stack.empty) // run task interpreter
    {
        task t = stack.pop();
        switch (t.tasktype)
        {
          case reccall:
            if (t.i == n) {/*display contents*/; break;}
            for (int j=t.i; j<n; j++)
            {
                stack.push(new task() {tasktype=swap, t.i, j});
                stack.push(new task() {tasktype=reccall, t.i+1});
                stack.push(new task() {tasktype=swap, t.i, j});
            }
            break;
          case swap:
            swap(v, t.i, t.j);
            break;
        }
    }
}

You need to read the chapter 7.2.1.2 of the TAOCP.


EDIT:
According to the discussion in the comments, a stack-based recursion elimination is good, too (although in my opinion it's not a pure iterative approach).
Here is a draft of a stack-based version:

void getPermutationsNR(int v[]) 
{
    struct task
    {
        enum { swap, reccall } tasktype;
        int i, j;
    }
    stack<task> stack;
    stack.push(new task() {tasktype=reccall, i=0}); // initial task
    while (!stack.empty) // run task interpreter
    {
        task t = stack.pop();
        switch (t.tasktype)
        {
          case reccall:
            if (t.i == n) {/*display contents*/; break;}
            for (int j=t.i; j<n; j++)
            {
                stack.push(new task() {tasktype=swap, t.i, j});
                stack.push(new task() {tasktype=reccall, t.i+1});
                stack.push(new task() {tasktype=swap, t.i, j});
            }
            break;
          case swap:
            swap(v, t.i, t.j);
            break;
        }
    }
}
墨落画卷 2024-12-01 02:30:30

您可以使用堆栈来使其迭代。在此堆栈中存储您的 j 变量。这应该有效。

void getPermutationsI(int v[], int n)
{
    int stack[100] = {0, 1, 2, 3, 4}; // you can do better, but I was lazy
    int k = 0;
    while (k >= 0)
    {
        if (k == n)
        {
            for (int i = 0; i < n; ++i)
                printf("%d ", v[i]);
            printf("\n");

            --k;
            swap(v[k], v[ stack[k] - 1 ]);
        }
        else
        {
            if (stack[k] < n)
            {
                swap(v[k], v[ stack[k] ]);
                ++stack[k];
                ++k;
            }
            else
            {
                stack[k] = k;
                --k;
                swap(v[k], v[ stack[k] - 1 ]);
            }
        }
    }
}

You can use a stack to make it iterative. In this stack you store your j variable. This should work.

void getPermutationsI(int v[], int n)
{
    int stack[100] = {0, 1, 2, 3, 4}; // you can do better, but I was lazy
    int k = 0;
    while (k >= 0)
    {
        if (k == n)
        {
            for (int i = 0; i < n; ++i)
                printf("%d ", v[i]);
            printf("\n");

            --k;
            swap(v[k], v[ stack[k] - 1 ]);
        }
        else
        {
            if (stack[k] < n)
            {
                swap(v[k], v[ stack[k] ]);
                ++stack[k];
                ++k;
            }
            else
            {
                stack[k] = k;
                --k;
                swap(v[k], v[ stack[k] - 1 ]);
            }
        }
    }
}
固执像三岁 2024-12-01 02:30:30

在Python中:

def perm_stack(s):
    st = []

    st.append((s,0))

    while not len(st)==0:

        t = st.pop()
        if t[1]==len(s):
            print t[0]
        else:
            for i in range(t[1], len(s)):
                s1 = swap(t[0], t[1], i)
                st.append((s1, t[1]+1))

In Python:

def perm_stack(s):
    st = []

    st.append((s,0))

    while not len(st)==0:

        t = st.pop()
        if t[1]==len(s):
            print t[0]
        else:
            for i in range(t[1], len(s)):
                s1 = swap(t[0], t[1], i)
                st.append((s1, t[1]+1))
吃不饱 2024-12-01 02:30:29

您遇到的挑战是函数调用和循环结构混合在一起。很难理清这些问题。

首先,我们首先用递归替换所有流操作的控制。

// You'll want to start with getPermutionsR(v, n, 0, 0)
void getPermutationsR(int v[], int n, int i, int j) 
{
    if (i == n)
    {
        //Display contents of v
    }
    else if (j == n) {
        // By doing nothing, we break out of the loop
    }
    else
    {
        // This was your recursive call inside of the loop.
        // Note that I'm sending you to to the first loop iteration here.
        swap (v, i, j);
        getPermutationsR(v, n, i+1, i+1);
        swap (v, i, j);

        // And the next loop iteration
        getPermutationsR(v, n, i, j+1);
    }
}

接下来,我们添加更多状态,以便 if 条件内只有一次递归调用。

// You'll want to start with getPermutionsR(v, n, 0, 0, 1)
void getPermutationsR(int v[], int n, int i, int j, bool firstCall)
{
    if (i == n)
    {
        //Display contents of v
    }

    int x = i;
    int y = j+1;
    if (firstCall) {
        swap (v, i, j);
        x = i+1;
        y = i+1;
    }

    // My one recursive call.  Note that i=n implies j=n.
    if (j < n) {
        getPermutationsR(v, n, x, y, !firstCall);
    }

    if (firstCall) {
        swap (v, i, j);
    }
}

既然我们已经完成了这一点,我们就可以以一种简单的方式将其转换为迭代版本的形式。这里是转换

void recursiveForm (params, recursiveState) {
    topHalf...
    if (cond) {
        recursiveForm(...)
    }
    bottomHalf...
}

变成

void iterativeForm(params) {
    initializeStacks...
    pushStacks...
    topHalf...
    while (stacks not empty) {
        if (cond) {
            pushStacks...
            topHalf...
        }
        else {
            bottomHalf...
            popStacks...
        }
    }
}

所以应用该模式我们得到类似的东西:(

// You'll want to start with getPermutionsR(v, n, 0, 0, 1)
void getPermutationsI(int v[], int n)
{
    stack<int> iStack;
    stack<int> jStack;
    stack<bool> firstCallStack;

    // pushStacks with initial value
    iStack.push(0);
    jStack.push(0);
    firstCallStack.push(1);

    // topHalf...
    if (iStack.top() == n)
    {
        //Display contents of v
    }

    int x = iStack.top();
    int y = jStack.top()+1;
    if (firstCallStack.top()) {
        swap (v, iStack.top(), jStack.top());
        x = iStack.top()+1;
        y = iStack.top()+1;
    }

    while iStack.size() > 0) {
        // if (cond) {
        if (jStack.top() < n) {
            //pushStacks...
            iStack.push(x);
            jStack.push(y);
            firstCallStack.push(!firstCallStack.top());

            // topHalf...
            if (iStack.top() == n)
            {
                //Display contents of v
            }

            x = iStack.top();
            y = jStack.top()+1;
            if (firstCallStack.top()) {
                swap (v, iStack.top(), jStack.top());
                x = iStack.top()+1;
                y = iStack.top()+1;
            }
        }
        else {
            // bottomHalf...
            if (firstCallStack.top()) {
                swap (v, iStack.top(), jStack.top());
            }
        }
    }
}

警告,所有代码都未经测试,甚至很可能无法编译。但这个想法是正确的。而且它绝对是相同的算法。)

The challenge you are encountering is that you've got function calls and loop constructs intermingled. It is hard to disentangle those.

First let's start by replacing all control of flow operations with recursion.

// You'll want to start with getPermutionsR(v, n, 0, 0)
void getPermutationsR(int v[], int n, int i, int j) 
{
    if (i == n)
    {
        //Display contents of v
    }
    else if (j == n) {
        // By doing nothing, we break out of the loop
    }
    else
    {
        // This was your recursive call inside of the loop.
        // Note that I'm sending you to to the first loop iteration here.
        swap (v, i, j);
        getPermutationsR(v, n, i+1, i+1);
        swap (v, i, j);

        // And the next loop iteration
        getPermutationsR(v, n, i, j+1);
    }
}

Next let's add more state so that we only have one recursive call inside of an if condition.

// You'll want to start with getPermutionsR(v, n, 0, 0, 1)
void getPermutationsR(int v[], int n, int i, int j, bool firstCall)
{
    if (i == n)
    {
        //Display contents of v
    }

    int x = i;
    int y = j+1;
    if (firstCall) {
        swap (v, i, j);
        x = i+1;
        y = i+1;
    }

    // My one recursive call.  Note that i=n implies j=n.
    if (j < n) {
        getPermutationsR(v, n, x, y, !firstCall);
    }

    if (firstCall) {
        swap (v, i, j);
    }
}

Now that we've accomplished this, we have it in a form where we can transform it into an iterative version in a straightforward way. Here is the transformation

void recursiveForm (params, recursiveState) {
    topHalf...
    if (cond) {
        recursiveForm(...)
    }
    bottomHalf...
}

becomes

void iterativeForm(params) {
    initializeStacks...
    pushStacks...
    topHalf...
    while (stacks not empty) {
        if (cond) {
            pushStacks...
            topHalf...
        }
        else {
            bottomHalf...
            popStacks...
        }
    }
}

So applying that pattern we get something like:

// You'll want to start with getPermutionsR(v, n, 0, 0, 1)
void getPermutationsI(int v[], int n)
{
    stack<int> iStack;
    stack<int> jStack;
    stack<bool> firstCallStack;

    // pushStacks with initial value
    iStack.push(0);
    jStack.push(0);
    firstCallStack.push(1);

    // topHalf...
    if (iStack.top() == n)
    {
        //Display contents of v
    }

    int x = iStack.top();
    int y = jStack.top()+1;
    if (firstCallStack.top()) {
        swap (v, iStack.top(), jStack.top());
        x = iStack.top()+1;
        y = iStack.top()+1;
    }

    while iStack.size() > 0) {
        // if (cond) {
        if (jStack.top() < n) {
            //pushStacks...
            iStack.push(x);
            jStack.push(y);
            firstCallStack.push(!firstCallStack.top());

            // topHalf...
            if (iStack.top() == n)
            {
                //Display contents of v
            }

            x = iStack.top();
            y = jStack.top()+1;
            if (firstCallStack.top()) {
                swap (v, iStack.top(), jStack.top());
                x = iStack.top()+1;
                y = iStack.top()+1;
            }
        }
        else {
            // bottomHalf...
            if (firstCallStack.top()) {
                swap (v, iStack.top(), jStack.top());
            }
        }
    }
}

(Warning, all code untested, and may well not even compile. But the idea is correct. And it is definitely the same algorithm.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文