Search code examples
c++for-loopnested-for-loop

compact form of many for loop in C++


I have a piece of code as follows, and the number of for loops is determined by n which is known at compile time. Each for loop iterates over the values 0 and 1. Currently, my code looks something like this

for(int in=0;in<2;in++){
    for(int in_1=0;in_1<2;in_1++){
        for(int in_2=0;in_2<2;in_2++){
          // ... n times
            for(int i2=0;i2<2;i2++){
               for(int i1=0;i1<2;i1++){
                   d[in][in_1][in_2]...[i2][i1] =updown(in)+updown(in_1)+...+updown(i1);
               }
            }
          // ...
        }
    }
}

Now my question is whether one can write it in a more compact form.


Solution

  • The n bits in_k can be interpreted as the representation of one integer less than 2^n.

    This allows easily to work with a 1-D array (vector) d[.].

    In practice, an interger j corresponds to

    j = in[0] + 2*in[1] + ... + 2^n-1*in[n-1]
    

    Moreover, a direct implementation is O(NlogN). (N = 2^n)

    A recursive solution is possible, for example using

    f(val, n) = updown(val%2) + f(val/2, n-1) and f(val, 0) = 0.
    

    This would correspond to a O(N) complexity, at the condition to introduce memoization, not implemented here.

    Result:

    0 : 0
    1 : 1
    2 : 1
    3 : 2
    4 : 1
    5 : 2
    6 : 2
    7 : 3
    8 : 1
    9 : 2
    10 : 2
    11 : 3
    12 : 2
    13 : 3
    14 : 3
    15 : 4
    
    
    #include <iostream>
    #include <vector>
    
    int up_down (int b) {
        if (b) return 1;
        return 0;
    }
    
    int f(int val, int n) {
        if (n < 0) return 0;
        return up_down (val%2) + f(val/2, n-1);
    }
    
    int main() {
        const int n = 4;
        int size = 1;
        for (int i = 0; i < n; ++i) size *= 2;
        std::vector<int> d(size, 0);
        
        for (int i = 0; i  < size; ++i) {
            d[i] = f(i, n);
        }
        for (int i = 0; i < size; ++i) {
            std::cout << i << " : " << d[i] << '\n';
        }
        return 0;
    }
    

    As mentioned above, the recursive approach allows a O(N) complexity, at the condition to implement memoization.

    Another possibility is to use a simple iterative approach, in order to get this O(N) complexity.
    (here N represents to total number of data)

    #include <iostream>
    #include <vector>
    
    int up_down (int b) {
        if (b) return 1;
        return 0;
    }
    int main() {
        const int n = 4;
        int size = 1;
        for (int i = 0; i < n; ++i) size *= 2;
        std::vector<int> d(size, 0);
        
        int size_block = 1;
        for (int i = 0; i  < n; ++i) {
            for (int j = size_block-1; j >= 0; --j) {
                d[2*j+1] = d[j] + up_down(1);
                d[2*j] = d[j] + up_down(0);
            }
            size_block *= 2;
        }
        for (int i = 0; i < size; ++i) {
            std::cout << i << " : " << d[i] << '\n';
        }
        return 0;
    }