You can use the greedy algorithm to enumerate all elements of the cartesian product 0:2 x 0:3 x 0:5. This algorithm is performed by my function greedy_backward
below. I am not an expert in Javascript and maybe this function could be improved.
function greedy_backward(sizes, n) {
for (var G = [1], i = 0; i<sizes.length; i++) G[i+1] = G[i] * sizes[i];
if (n>=_.last(G)) throw new Error("n must be <" + _.last(G));
for (i = 0; i<sizes.length; i++) if (sizes[i]!=parseInt(sizes[i]) || sizes[i]<1){ throw new Error("sizes must be a vector of integers be >1"); };
for (var epsilon=[], i=0; i < sizes.length; i++) epsilon[i]=0;
while(n > 0){
var k = _.findIndex(G, function(x){ return n < x; }) - 1;
var e = (n/G[k])>>0;
epsilon[k] = e;
n = n-e*G[k];
}
return epsilon;
}
It enumerates the elements of the Cartesian product in the anti-lexicographic order (you will see the full enumeration in the doSomething
example):
~ var sizes = [2, 3, 5];
~ greedy_backward(sizes,0);
0,0,0
~ greedy_backward(sizes,1);
1,0,0
~ greedy_backward(sizes,2);
0,1,0
~ greedy_backward(sizes,3);
1,1,0
~ greedy_backward(sizes,4);
0,2,0
~ greedy_backward(sizes,5);
1,2,0
This is a generalization of the binary representation (the case when sizes=[2,2,2,...]
).
Example:
~ function doSomething(v){
for (var message = v[0], i = 1; i<v.length; i++) message = message + '-' + v[i].toString();
console.log(message);
}
~ doSomething(["a","b","c"])
a-b-c
~ for (var max = [1], i = 0; i<sizes.length; i++) max = max * sizes[i];
30
~ for(i=0; i<max; i++){
doSomething(greedy_backward(sizes,i));
}
0-0-0
1-0-0
0-1-0
1-1-0
0-2-0
1-2-0
0-0-1
1-0-1
0-1-1
1-1-1
0-2-1
1-2-1
0-0-2
1-0-2
0-1-2
1-1-2
0-2-2
1-2-2
0-0-3
1-0-3
0-1-3
1-1-3
0-2-3
1-2-3
0-0-4
1-0-4
0-1-4
1-1-4
0-2-4
1-2-4
If needed, the reverse operation is simple:
function greedy_forward(sizes, epsilon) {
if (sizes.length!=epsilon.length) throw new Error("sizes and epsilon must have the same length");
for (i = 0; i<sizes.length; i++) if (epsilon[i] <0 || epsilon[i] >= sizes[i]){ throw new Error("condition `0 <= epsilon[i] < sizes[i]` not fulfilled for all i"); };
for (var G = [1], i = 0; i<sizes.length-1; i++) G[i+1] = G[i] * sizes[i];
for (var n = 0, i = 0; i<sizes.length; i++) n += G[i] * epsilon[i];
return n;
}
Example :
~ epsilon = greedy_backward(sizes, 29)
1,2,4
~ greedy_forward(sizes, epsilon)
29