In C++ I wish to iterate an n-dimensional array with arbitrary extents ranging from min[n] to max[n] respectively, maintaining the ordinates in ord[n] respectively throughout.
Ie. a general solution to:
for (int x = 0; x < 10; x++)
for (int y = 3; y < 20; y++)
for (int z = -2; z < 5; z++)
...
doSomething(x, y, z ...)
Of the form:
int min[n] {0, 3, -2 ...}
int max[n] {10, 20, 5 ...}
int ord[n] {0, 0, 0 ...};
int maxIterations = (max[0] - min[0]) * (max[1] - min[1]) * ....
for (int iteration = 0; iteration < maxIterations; iteration++)
doSomething(ord)
iterate(n, ord, min, max)
The fastest algorithm for iterate() I can think of is:
inline void iterate(int dimensions, int* ordinates, int* minimums, int* maximums)
{
// iterate over dimensions in reverse...
for (int dimension = dimensions - 1; dimension >= 0; dimension--)
{
if (ordinates[dimension] < maximums[dimension])
{
// If this dimension can handle another increment... then done.
ordinates[dimension]++;
break;
}
// Otherwise, reset this dimension and bubble up to the next dimension to take a look
ordinates[dimension] = minimums[dimension];
}
}
This increments and resets each ordinate as required, avoiding the callstack or any maths.
Is there are faster algorithm?
Unless you start doing something analogous to Gray codes which is going to change the order of your traversal (and potentially be very complex), you're pretty much at a point where it's as good as it's going to get. Actually, the amortized time of
iterate
is alreadyO(1)
, assuming each dimension has a minimum that is not equal to its maximum.The worst case is when all
d
dimensions havemaximum = minimum + 1
. That is, every other increment of any particular dimension will spill into the next dimension(s). However, note that the total number of digit changes needed for a specific dimensionx
(from1
tod
) is2^(d + 1 - x) - 1
. This is obviously less than2^(d + 1 - x)
. Summing this over all dimensions (1
throughd
) is a simple geometric sum which yields2^(d + 1) - 2
which is obviously less than2^(d + 1)
. Note the number of iterations is2^d
, and hence the average time per iteration is a constant:2^(d + 1) / 2^d = 2
.If you really have to squeeze speed, probably the best it's going to get is low level tweaks:
for
loop by unrolling the loop. Your compiler may be smart enough to do this already if it can deduce thatdimensions
is constant, or you may have to create several versions ofiterate
with a constant dimension or with the loop unrolled manually. (You could use templating if you wanted to give it a nice API.)doSomething
call in it) and allow your iterate function to change a boolean when it runs out of dimensions it can increase. This reduces yourfor
loop towhile (keepGoing) { ... }
.Of course, benchmark before and after any such changes -- every architecture and tool chain reacts differently.