11

How can I iterate over a n-dimensional array given the number of dimensions and the size of each as variables?

int n;
int size[n];

Since the number of dimensions is not fixed, I cannot write a nested loop for each dimension. I need the code to work with each number of dimensions.

In addition, it doesn't matter weather the actual data is stored in a n-dimensional array or a flat array containing all the data in a large row. Both are acceptable.

int data[16][42][14];   // n-dimensional array
int data[16 * 42 * 14]; // flat array containing the same data
danijar
  • 32,406
  • 45
  • 166
  • 297
  • What do you want to do with these `n`-dimensions? I suppose you need to know this and therefore you also know the amount of dimensions... – Veger Dec 26 '12 at 11:59
  • The number of dimensions vary, you say? That seems rather tricky to solve, since you will need to know the number of dimensions to know the size of each dimension below. I've not tried it, but I guess you could write some horrible recursive code to do this... – Mats Petersson Dec 26 '12 at 12:00
  • The number of the dimensions vary but the size of each dimension is known. – danijar Dec 26 '12 at 12:24
  • What do you mean by "iterate over the array"? Look at each element? Look at each element *with its indices*? Or, as with amit's answer, just iterate over all the vectors of indices? If it's the first one, you can just iterate over the flat array, by first calculating the flat size as the product of the size[] array. – rici Dec 26 '12 at 17:44

5 Answers5

8

You could use recursion, for each dimension "guess" its index and recursively invoke on a smaller problem, something along the lines of (peudo code):

iterate(d,n,size,res):
   if (d >= n): //stop clause
       print res
       return
   for each i from 0 to size[d]:
       res.append(i) //append the "guess" for this dimension
       iterate(d+1,n,size,res)
       res.removeLast //clean up environment before next iteration

where:

  • d is the currently visited dimension
  • size,n is the input
  • res is a vector representing the current partial result

invoke with iterate(0,n,size,res), where res is initialized to an empty list.


C++ code should be something like:

void iterate(int d,int n,int size[], int res[]) {
    if (d >= n) { //stop clause
       print(res,n);
       return;
   }
   for (int i = 0; i < size[d]; i++) { 
       res[d] = i;
       iterate(d+1,n,size,res);
   }
}

full code and a simple example are available on ideone

amit
  • 175,853
  • 27
  • 231
  • 333
  • Great pseudo-code. I'd be interested to see how you do the translation of the size[n] flat array tho' - I was about to suggest something like that, but couldn't be bothered to do a flat to multi-dimensional array conversion. – Mats Petersson Dec 26 '12 at 12:03
  • With C++ you can probably do the recursion compile-time (instead of runtime) using templates. However, this requires that you now the number of dimensions also at compile-time. – muksie Dec 26 '12 at 12:06
5

Python code:

def nd_range(start, stop, dims):
  if not dims:
    yield ()
    return
  for outer in nd_range(start, stop, dims - 1):
    for inner in range(start, stop):
      yield outer + (inner,)

Example:

print(list(nd_range(0, 3, 3)))

[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 2, 0), (0, 2, 1), (0, 2, 2), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 2, 0), (1, 2, 1), (1, 2, 2), (2, 0, 0), (2, 0, 1), (2, 0, 2), (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 2, 0), (2, 2, 1), (2, 2, 2)]

danijar
  • 32,406
  • 45
  • 166
  • 297
1

You could use recursion. Here is a pseudocode solution for nested arrays:

iterate_n(array, n)
    if n == 0
        do something with the element
    else
        for ary in array
            iterate_n(ary, n-1)
        end_for
    end_if
end
Huluk
  • 864
  • 6
  • 18
1

To list all combinations, in JavaScript, without recursion:

function listCoords(dimensions) {
    var cumulatives = new Array(dimensions.length);
    var total = 1;
    for (var d = dimensions.length - 1; d >= 0; d--) {
        cumulatives[d] = total;
        total *= dimensions[d];
    }
    var coords = new Array(total);
    for (var i = 0; i < total; i++) {
        var coord = new Array(dimensions.length);
        for (var d = dimensions.length - 1; d >= 0; d--) {
            coord[d] = Math.floor(i / cumulatives[d]) % dimensions[d];
        }
        coords[i] = coord;
    }
    return coords;
}

Examples:

  • [2, 3] returns [[0,0],[0,1],[0,2],[1,0],[1,1],[1,2]]
  • [3, 2] returns [[0,0],[0,1],[1,0],[1,1],[2,0],[2,1]]
  • [2, 2, 2] returns [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]]
0

Here's how I did it in MATLAB using recursion:

function out=iterate(data,func,dim)
% Usage: out=iterate(data,func)
% This function individually applies the user defined function 'func' to 
% every element of the array 'data'.
% The third input 'dim' is for internal recursive use only, after global 
% setup variables have been initialized.


global sz inds g_out
% 'sz' is size(data) with singleton dimensions removed
% 'inds' is an array of size [1 length(sz)] containing the current indices
% 'g_out' is where parts of the output are accumulated throughout iteration

if nargin<3
    %Setup 'inds' 'sz' 'g_out'
    dim=1;  %'dim' is the current dimension to iterate through
    sz=size(data);
    if sz(2)==1
        sz=sz(1);
    end
    inds=ones(1,length(sz));

    %Initialize the output as the proper class
    %Depends on the output of the user given function
    switch class(func(data(1)))
        case 'logical'
            g_out= false(sz);
        case 'double'
            g_out=double(zeros(sz));
        case 'char'
            g_out=repmat(' ',sz);
        otherwise
            g_out=cast(zeros(sz),class(func(data(1)))); %#ok<ZEROLIKE>
    end
end

for i=1:sz(dim)
    inds(dim)=i;
    ndx=subs2ind(sz,inds);
    if dim<length(sz)
        iterate(data,func,dim+1);
    else
        g_out(ndx)=func(data(ndx));
    end
end
out=g_out;
end