创建 N 个嵌套的 for 循环

20

有没有一种方式可以创建类似于以下形式的for循环

for(int i = 0; i < 9; ++i) {
    for(int j = 0; j < 9; ++i) {
    //...
        for(int k = 0; k < 9; ++k) { //N-th loop

在编译时不知道N的情况下,我正在尝试找出一种方法来循环遍历数字向量的单独元素,以创建每个可能的数字,如果某些数字被替换为不同的数字,则创建的数字将包含相同数量的数字。


3
你可以在一个包含for循环的函数中使用N次递归调用。 - πάντα ῥεῖ
7个回答

16

你可以使用递归并设置基本条件 -

void doRecursion(int baseCondition){

   if(baseCondition==0) return;

   //place your code here

   doRecursion(baseCondition-1);
}  

现在你不需要在编译时提供baseCondition的值。你可以在调用doRecursion()方法时提供它。


为什么if和else之间会有一个空格?我不确定它是如何工作的。 - Andrew
@Andrew,谢谢。我已经修复了它。实际上,一开始没有注释(//在此处放置您的代码)部分。添加注释后,我忘记删除else - Razib

5
这里有一个不错的小类,用于多索引,可以通过基于范围的for循环进行迭代:
#include<array>

template<int dim>
struct multi_index_t
{
    std::array<int, dim> size_array;
    template<typename ... Args>
    multi_index_t(Args&& ... args) : size_array(std::forward<Args>(args) ...) {}

    struct iterator
    {
        struct sentinel_t {};

        std::array<int, dim> index_array = {};
        std::array<int, dim> const& size_array;
        bool _end = false;

        iterator(std::array<int, dim> const& size_array) : size_array(size_array) {}

        auto& operator++()
        {
            for (int i = 0;i < dim;++i)
            {
                if (index_array[i] < size_array[i] - 1)
                {
                    ++index_array[i];
                    for (int j = 0;j < i;++j)
                    {
                        index_array[j] = 0;
                    }
                    return *this;
                }
            }
            _end = true;
            return *this;
        }
        auto& operator*()
        {
            return index_array;
        }
        bool operator!=(sentinel_t) const
        {
            return !_end;
        }
    };

    auto begin() const
    {
        return iterator{ size_array };
    }
    auto end() const
    {
        return typename iterator::sentinel_t{};
    }
};

template<typename ... index_t>
auto multi_index(index_t&& ... index)
{
    static constexpr int size = sizeof ... (index_t); 
    auto ar = std::array<int, size>{std::forward<index_t>(index) ...};
    return multi_index_t<size>(ar);
}

基本思路是使用一个数组来保存一定数量的dim索引,然后实现operator++以适当地增加这些索引。
使用方法如下:
for(auto m : multi_index(3,3,4))
{
    // now m[i] holds index of i-th loop
    // m[0] goes from 0 to 2
    // m[1] goes from 0 to 2
    // m[2] goes from 0 to 3
    std::cout<<m[0]<<" "<<m[1]<<" "<<m[2]<<std::endl;
}

在 Coliru 上实时运行


在向量中持有min_ind和max_ind的原因是什么?使用int min_ind和int max_ind不会更容易吗? - Andrew
如果这对你来说足够了,就像这样做。但是,第一个循环可能从3到6,第二个循环可能从1到8,依此类推。 - davidhigh

4
你可以使用递归函数:
void loop_function(/*params*/,int N){
for(int i=0;i<9;++i){
    if(N>0) loop_function(/*new params*/,N-1);
}

这将递归调用循环函数N次,而每个函数都会迭代调用循环函数。
这种编程方式可能有点难度,但它应该可以实现你所需求的功能。

当我尝试运行"int counter = 0;",void loop_function(vector<int>& digits,int n)时,我得到的计数器counter值为819,但它肯定应该是999吧?主函数的代码包含了一个for循环和一个递归调用loop_function函数,并在每次递归完成后增加计数器counter的值。 - Andrew
根据我理解你的代码,你的向量数字会一直变化,直到向量变为{9,9,9}。为了实现这个目标,你对最后一个元素执行了999次迭代(在两个循环内从0到9),对于第二个元素,你执行了99次迭代,最后再进行9次迭代。因此,最终你需要执行总共999+99+9=819次迭代,这也是你所说的计数器。 - angrykoala
我正在尝试创建从1到999的每个元素。在代码中我需要更改什么? - Andrew
每次调用函数时,您都会重新启动该位置元素。我不明白您想要什么,如果您想在最小迭代次数(2997)中获得{999,999,999},则嵌套循环似乎不是一个好选择,因为您正在使问题变得过于复杂。如果您想像现在这样做(导致998001999次迭代),只需将9更改为999即可,但显然这不是一个好主意。当您嵌套循环时,最后一个循环将比前一个循环多迭代n次,因此如果您不重新启动计数并保持原样,您将在819次迭代中获得{9,81,719}作为结果。 - angrykoala

2
您可以使用递归调用,如下所示:

void runNextNestedFor(std::vector<int> counters, int index)
{
     for(counters[index] = 0; counters[index] < 9; ++counters[index]) {
       // DO
       if(index!=N)
          runNextNestedFor(counters, index+1);
     }
}

第一次调用时,请使用以下方式:

std::vectors<int> counters(N);
runNextNestedFor(counters, 0);

1

我写了一些实现 N-nested for 循环的 C++ 11 代码,以下是可以作为单个 .hpp 导入的主要部分(我将其命名为 nestedLoop.hpp):

#ifndef NESTEDLOOP_HPP
#define NESTEDLOOP_HPP
#include <vector>

namespace nestedLoop{

    class nestedLoop {
        public:
            //Variables
            std::vector<int> maxes;
            std::vector<int> idxes; //The last element is used for boundary control
            int N=0;
            int nestLevel=0;

            nestedLoop();
            nestedLoop(int,int);
            nestedLoop(int,std::vector<int>);

            void reset(int numberOfNests, int Max);
            void reset(int numberOfNests, std::vector<int> theMaxes);

            bool next();
            void jumpNest(int theNest);

        private:
            void clear();
    };

    //Initialisations
    nestedLoop::nestedLoop(){}

    nestedLoop::nestedLoop(int numberOfNests, int Max) {
        reset(numberOfNests, Max);
    }

    nestedLoop::nestedLoop(int numberOfNests, std::vector<int> theMaxes) {
        reset(numberOfNests,  theMaxes);
    }

    void nestedLoop::clear(){
        maxes.clear();
        idxes.clear();
        N = 0;
        nestLevel = 0;
    }

    //Reset the scene
    void nestedLoop::reset(int numberOfNests, int Max){
        std::vector<int> theMaxes;
        for(int i =0; i < numberOfNests; i++) theMaxes.push_back(Max);
        reset(numberOfNests, theMaxes);
    }

    void nestedLoop::reset(int numberOfNests, std::vector<int> theMaxes){
        clear();
        N = numberOfNests;

        maxes=theMaxes;

        idxes.push_back(-1);
        for(int i=1; i<N; i++) idxes.push_back(theMaxes[i]-1);
    }

    bool nestedLoop::next(){
        idxes[N-1]+=1;

        for(int i=N-1; i>=0; i--){
            if(idxes[i]>=maxes[i]) {
                idxes[i] = 0;

                if(i){ //actually, if i > 0 is needed
                    idxes[i-1] += 1;
                }else{
                    return false;
                }
            }else{
                nestLevel = i;
                break;
            }
        }
        return true;
    }

    void nestedLoop::jumpNest(int theNest){
        for(int i = N-1; i>theNest; i--) {
            idxes[i] = maxes[i]-1;
        }
    }
}
#endif // NESTEDLOOP_HPP

这里是一个带有预期输出的例子:

#include <iostream>
#include "stlvecs.hpp"
#include "nestedLoop.hpp"

int main(){
    nestedLoop::nestedLoop looper;
    std::vector<int> maxes = {2, 3, 2, 2};
    looper.reset(4,maxes);
    int i = 0;
    while(looper.next()){
        std::cout << "Indices: " << looper.idxes << ", Last nest incremented: " << looper.nestLevel << std::endl;
        if(i == 5){
            std::cout << "...Jump Second Nest (index 1)..." << std::endl;
            looper.jumpNest(1);
        }
        i++;
    }
}

/* Expected output
Indices: 4  0 0 0 0 , Last nest incremented: 0
Indices: 4  0 0 0 1 , Last nest incremented: 3
Indices: 4  0 0 1 0 , Last nest incremented: 2
Indices: 4  0 0 1 1 , Last nest incremented: 3
Indices: 4  0 1 0 0 , Last nest incremented: 1
Indices: 4  0 1 0 1 , Last nest incremented: 3
...Jump Second Nest (index 1)...
Indices: 4  0 2 0 0 , Last nest incremented: 1
Indices: 4  0 2 0 1 , Last nest incremented: 3
Indices: 4  0 2 1 0 , Last nest incremented: 2
Indices: 4  0 2 1 1 , Last nest incremented: 3
Indices: 4  1 0 0 0 , Last nest incremented: 0
Indices: 4  1 0 0 1 , Last nest incremented: 3
Indices: 4  1 0 1 0 , Last nest incremented: 2
Indices: 4  1 0 1 1 , Last nest incremented: 3
Indices: 4  1 1 0 0 , Last nest incremented: 1
Indices: 4  1 1 0 1 , Last nest incremented: 3
Indices: 4  1 1 1 0 , Last nest incremented: 2
Indices: 4  1 1 1 1 , Last nest incremented: 3
Indices: 4  1 2 0 0 , Last nest incremented: 1
Indices: 4  1 2 0 1 , Last nest incremented: 3
Indices: 4  1 2 1 0 , Last nest incremented: 2
Indices: 4  1 2 1 1 , Last nest incremented: 3
*/

好的答案!对我来说是即插即用的。 - Luke Rodgers

1
我将按照提供的示例代码接受OP,并假设要求解决方案计算任意十进制数字。 (我基于评论“理想情况下,我正在尝试找出一种方法来循环遍历数字向量的单独元素以创建每个可能的数字”。)
这个解决方案有一个循环,它在十进制下计数数字向量,并将每个连续的值传递到一个帮助函数(doThingWithNumber)中。为了测试目的,我让这个辅助函数简单地打印出数字。
#include <iostream>

using namespace std;

void doThingWithNumber(const int* digits, int numDigits)
{
    int i;
    for (i = numDigits-1; i>=0; i--)
        cout << digits[i];
    cout << endl;
}

void loopOverAllNumbers(int numDigits)
{
    int* digits = new int [numDigits];
    int i;
    for (i = 0; i< numDigits; i++) 
        digits[i] = 0;

    int maxDigit = 0;
    while (maxDigit < numDigits) {
        doThingWithNumber(digits, numDigits);
        for (i = 0; i < numDigits; i++) {
            digits[i]++;
            if (digits[i] < 10)
                break;
            digits[i] = 0;
        }
        if (i > maxDigit)
            maxDigit = i;
    }
}

int main()
{
    loopOverAllNumbers(3);
    return 0;
}

0
我使用这个解决方案:
unsigned int dim = 3;
unsigned int top = 5;
std::vector<unsigned int> m(dim, 0);
for (unsigned int i = 0; i < pow(top,dim); i++)
{
    // What you want to do comes here 
    //      |
    //      |
    //      v
    // -----------------------------------
    for (unsigned int j = 0; j < dim; j++)
    {
        std::cout << m[j] << ",";
    }
    std::cout << std::endl;
    // -----------------------------------

    // Increment m
    if (i == pow(top, dim) - 1) break;
    unsigned int index_to_increment = dim - 1;
    while(m[index_to_increment] == (top-1)) {
        m[index_to_increment] = 0;
        index_to_increment -= 1;
    }
    m[index_to_increment] += 1;
}

它肯定可以进行优化和适应,但它运行得非常好,而且您不需要将参数传递给递归函数。使用单独的函数来增加多索引:

typedef std::vector<unsigned int> ivec;
void increment_multi_index(ivec &m, ivec const & upper_bounds)
{
    unsigned int dim = m.size();
    unsigned int i = dim - 1;
    while(m[i] == upper_bounds[i] - 1 && i>0) {
        m[i] = 0;
        i -= 1;
    }
    m[i] += 1;
}

int main() {

    unsigned int dim = 3;
    unsigned int top = 5;
    ivec m(dim, 0);
    ivec t(dim, top);
    for (unsigned int i = 0; i < pow(top,dim); i++)
    {
        // What you want to do comes here 
        //      |
        //      |
        //      v
        // -----------------------------------
        for (unsigned int j = 0; j < dim; j++)
        {
            std::cout << m[j] << ",";
        }
        std::cout << std::endl;
        // -----------------------------------

        // Increment m
        increment_multi_index(m, t);
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接