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
iterateis alreadyO(1), assuming each dimension has a minimum that is not equal to its maximum.The worst case is when all
ddimensions 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(from1tod) is2^(d + 1 - x) - 1. This is obviously less than2^(d + 1 - x). Summing this over all dimensions (1throughd) is a simple geometric sum which yields2^(d + 1) - 2which 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:
forloop by unrolling the loop. Your compiler may be smart enough to do this already if it can deduce thatdimensionsis constant, or you may have to create several versions ofiteratewith a constant dimension or with the loop unrolled manually. (You could use templating if you wanted to give it a nice API.)doSomethingcall in it) and allow your iterate function to change a boolean when it runs out of dimensions it can increase. This reduces yourforloop towhile (keepGoing) { ... }.Of course, benchmark before and after any such changes -- every architecture and tool chain reacts differently.