簡體   English   中英

旋轉遞歸算法以將所有組合生成為迭代

[英]Turning recursive algorithm to generate all combinations into an iterative

誰能幫助我將這種算法變成一種迭代算法。 我知道遞歸只是迭代加上堆棧,但是到目前為止,我還沒有提出合適的解決方案。

void rGenCombs(int n, int k, vector<int>& chosen, int idx,
               vector<vector<int>>& combs) {
    if (chosen.size() == k) {
        combs.push_back(chosen);
        return;
    }
    for (int i = idx; i <= n; ++i) {
        chosen.push_back(i);
        rGenCombs(n, k, chosen, i + 1, combs);
        chosen.pop_back();
    }
}

vector<vector<int>> genCombsRec(int n, int k) {
    vector<vector<int>> combs;
    vector<int> chosen;
    rGenCombs(n, k, chosen, 1, combs);
    return combs;
}

更新我現在有這個。 問題是我不知道該寫哪個循環。 我猜應該可以通過一個簡單的while循環以某種方式實現。

vector<vector<int>> genCombs(int n, int k) {
    vector<int> numStack, chosen;
    vector<vector<int>> combs;
    numStack.push_back(1);
    while (!numStack.empty()) {
        if (chosen.size() == k) {
            combs.push_back(chosen);
            chosen.pop_back();
            continue;
        }
        chosen.push_back(numStack.back());
        if (numStack.back() <= n) {
            numStack.push_back(numStack.back() + 1);
        } else {
            numStack.pop_back();
        }
    }
    return combs;
}

解決方案

對於不需要堆棧的不同迭代算法,我得出以下結果:

int getNextIncIndex(const vector<int>& combs, int n) {
    int k = static_cast<int>(combs.size());
    for (int i = k - 1; i >= 0; --i) {
        int distFromRight = k - i - 1;
        if (combs[i] < n - distFromRight) {
            return i;
        }
    }
    return -1;
}

vector<vector<int>> genCombs(int n, int k) {
    vector<vector<int>> combs;
    vector<int> comb(k, 1);
    iota(comb.begin(), comb.end(), 1);
    while (true) {
        for (int i = comb[k - 1]; i <= n ; ++i) {
            comb[k - 1] = i;
            combs.push_back(comb);
        }
        int incIdx = getNextIncIndex(comb, n);
        if (incIdx == -1) {
            break;
        } else {
            iota(comb.begin() + incIdx, comb.end(), comb[incIdx] + 1);
        }
    }
    return combs;
}

我不會給你答案,但是我會給你關鍵技巧,以合理的方式來做。

您的思維障礙的一部分是,您有兩種類型的控制交織在一起。 第一個是您的for循環。 第二個是遞歸。 您如何跳出for循環的內部進入外部循環並遞歸,而又回到for循環的內部? 很容易感到困惑。

而是引入一個堆棧,而不是兩個堆棧。 一個堆棧是用來跟蹤您需要執行的操作。 另一個用於呼叫幀中的所有數據。 最終代碼的關鍵部分如下所示:

while (0 < actions.size()) {
    action thisAction = actions.pop_back();
    switch (thisAction.type) {
        case ENTER_FUNCTION:
            ...
            break;
        case ENTER_LOOP:
            ...
            break;
        case EXIT_LOOP:
            ...
            break;
        case EXIT_FUNCTION:
            ...
            break;
    }
}

現在,您可以以統一的方式跟蹤循環和函數調用。 沒有更多的混亂。

這是每個部分的內容。

  • ENTER_FUNCTION :檢查是否存在,確定是否有循環,然后將其設置為開始並將ENTER_LOOP附加到操作堆棧上。 (如果不循環,則執行if。)
  • ENTER_LOOP :測試循環條件。 如果匹配,則設置循環,然后在操作堆棧上附加ENTER_LOOPEXIT_LOOPEXIT_FUNCTIONENTER FUNCTION (請注意,堆棧中的最后一項將首先發生。)然后在調用堆棧上添加函數調用的參數,以便在進行遞歸調用時它們就在其中。
  • EXIT_LOOP :執行chosen.pop_back()並增加當前調用框架中的i (這很重要,呼叫幀需要分開存放!)
  • EXIT_FUNCTION :擺脫頂部調用框架,它已完成。

一旦您認為自己了解這種策略,就去學習Forth :-)

如果您只需要迭代算法,那么我認為您的方向錯誤。 確實不需要堆棧。

如果出於某種原因確實需要一個堆棧,請忽略休息。

為了便於說明,我以n=6, k=3運行您的代碼:

1 2 3 
1 2 4 
1 2 5 
1 2 6 
1 3 4 
1 3 5 
1 3 6 
1 4 5 
1 4 6 
1 5 6 
2 3 4 
2 3 5 
2 3 6 
2 4 5 
2 4 6 
2 5 6 
3 4 5 
3 4 6 
3 5 6 
4 5 6 

您會看到一個簡單的模式,它導致了簡單的算法:

  • 采取最后位置。 增加它。 這將給出下一個組合。

  • 一旦達到最高點,就不能再增加該位置,增加下一個“可遞增”位置,然后將std::iota移到右邊。

  • 重新開始,繼續前進直到不再有可遞增的位置。

一個非常骯臟但可行的實現,還有大量的改進空間:

#include <numeric>

int find_incrementable(std::vector<int>& current, int n)
{
    int pos;
    current.push_back(n + 1);   // Dirty hack
    for (pos = current.size() - 2; pos >= 0; --pos) {
        if (current[pos] + 1 < current[pos + 1]) {
            break;
        }
    }
    current.pop_back();
    return pos;
}

std::vector<std::vector<int>> genCombsIter(int n, int k)
{
    std::vector<std::vector<int>> combs;
    std::vector<int> current(k);
    std::iota(current.begin(), current.end(), 1);
    combs.push_back(current);

    int position = k - 1;
    int incrementable;
    while ((incrementable = find_incrementable(current, n)) >= 0) {
        if (incrementable == position) {
            current[position] += 1;
        } else {
            if (incrementable == -1) {
                break;
            }
            std::iota(current.begin() + incrementable, current.end(), current[incrementable] + 1);
            position = k - 1;
        }
        combs.push_back(current);
    }
    return combs;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM