Nested for loops - how to ignore certain combinations?
I'm doing some kind of brute force attack to solve a problem. Theoratically it is a working solution, it also really is, but it takes rather long.
I have 7 nested for loops within each other, but I only need the combinations of the 'for variables' in which none are repeated. So e.g. 1,2,3,4,5,6,7 is allowed, but 1,1,3,4,5,6,7 should be ignored. I'm currently using a function to check for duplicate items in an array:
http://www.groggyjava.tv/freebies/duplicates.html
However I think I'd be better off if I could immediately ignore those combinations instead of using that function over and over for every single combination. Now I'm evaluating 14^7 = 105.413.504 combinations, but only 14 nPr 7 = 17.297.280 of them are necessary.
My code currently is (in which hgd is the duplicate test function, with returns true if no duplicates):
for(var a=0;a<14;a++) {
for(var b=0;b<14;b++) {if(hgd([a,b])) {
for(var c=0;c<14;c++) {if(hgd([a,b,c])) {
for(var d=0;d<14;d++) {if(hgd([a,b,c,d])) {
for(var e=0;e<14;e++) {if(hgd([a,b,c,d,e])) {
for(var f=0;f<14;f++) {if(hgd([a,b,c,d,e,f])) {
for(var g=0;g<14;g++) {if(hgd([a,b,c,d,e,f,g])) {
check([a,b,c,d,e,f,g]);
}}
}}
}}
开发者_JAVA百科}}
}}
}}
}
How could I make this faster, is there another solution instead of for loops perhaps?
Thanks.
What you're doing here is iterating over all the permutations of a list of 14 discreet values.
In general, to visit all the permutations of a list of discreet things, you visit each element of the list. For each element, form a new list containing all other elements of the original list. Get all the permutations of that list, prepend the element to each, and you've got all the permutations of the original list that can start with that particular element. When you've done all the elements, you're done.
Finding all permutations of a list with one element in it is a simple task, of course.
Thus what we've got is something like this:
function forEachPermutation(list, operation) {
function pluckElement(list, index) {
var e = list[index];
var l = [];
for (var i = 0; i < list.length; ++i)
if (i !== index) l.push(list[i]);
return { element: e, remainder: l };
}
function permute(partial, remainder) {
if (remainder.length === 0)
operation(partial);
else {
for (var i = 0; i < remainder.length; ++i) {
var plucked = pluckElement(remainder, i);
partial.push(plucked.element);
permute(partial, plucked.remainder);
partial.length--;
}
}
}
permute([], list);
}
What that does is recursively carry out the operation I described above. The "pluckElement" function returns an element from a list, and the rest of that list. The "permute" function then either carries out the operation passed in on a complete permutation of the original list (when it notices that the remainder list is empty), or else calls itself recursively with each element of the list it's passed.
To use that function, you'd just do this:
forEachPermutation([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], check);
edit — if you want to be plucking only a certain number of values from the original list, then the above would have to be modified; give me a sec.
OK - if you want to pass in a list of (say) 14 elements, but have the "operation" function invoked for each distinct list of (say) 7 of the 14, you could modify the function as follows. The outer "forEachPermutation" function would take an additional "len" parameter, to tell it the length of string desired. Then, the "permute" function would check to see if "partial.length" is that target length, instead of checking for an empty remainder.
function forEachPermutation(list, len, operation) {
function pluckElement(list, index) {
var e = list[index];
var l = [];
for (var i = 0; i < list.length; ++i)
if (i !== index) l.push(list[i]);
return { element: e, remainder: l };
}
function permute(partial, remainder) {
if (partial.length === len)
operation(partial);
else {
for (var i = 0; i < remainder.length; ++i) {
var plucked = pluckElement(remainder, i);
partial.push(plucked.element);
permute(partial, plucked.remainder);
partial.length--;
}
}
}
permute([], list);
}
and you'd call it with
forEachPermutation([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], 7, check);
Another edit — if for some reason you want to save the permutations after the "operation" function processes each one, then we'll have to account for the fact that the partial array being used gets overwritten. The call to "operation" can be changed:
if (partial.length === len)
operation(partial.slice(0));
That makes a copy of the "partial" array, so that each invocation of "operation" gets its very own array to use. It'll make the process slower, of course.
The problem is that you are doing extra work. In your c
loop you already know that a
and b
are not equal, but you check anyway. You could exploit the flexibility of c-style for
loops:
for(var a=0;a<14;a++) {
for(var b=0;b<14 && b!=a;b++) {
for(var c=0;c<14 && c!=a && c!=b;c++) {
...
}
}
}
This gets a little verbose in the inner loop, but is at least simple.
精彩评论