嵌套for循环 - 如何忽略某些组合?

时间:2010-12-13 16:50:49

标签: javascript

我正在做某种暴力攻击来解决问题。理论上它是一个有效的解决方案,它确实是,但它需要相当长的时间。

我有7个嵌套for循环,但我只需要'for variables'的组合,其中没有重复。所以例如允许使用1,2,3,4,5,6,7,但应忽略1,1,3,4,5,6,7。我目前正在使用一个函数来检查数组中的重复项:

http://www.groggyjava.tv/freebies/duplicates.html

但是我认为如果我能立即忽略这些组合而不是反复使用每个单一组合的那个功能,我会更好。现在我正在评估14 ^ 7 = 105.413.504组合,但只有14 nPr 7 = 17.297.280组合是必要的。

我的代码目前是(其中hgd是重复的测试函数,如果没有重复,则返回true):

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]);
                        }}
                    }}
                }}
            }}
        }}
    }}
}

我怎样才能让这个更快,是否有另一个解决方案而不是for循环呢?

感谢。

2 个答案:

答案 0 :(得分:5)

你在这里做的是迭代14个谨慎值列表的所有排列

通常,要访问谨慎事物列表的所有排列,您可以访问列表中的每个元素。对于每个元素,形成一个包含原始列表的所有其他元素的新列表。获取 列表的所有排列,将元素添加到每个列表中,并且您已获得可以从该特定元素开始的原始列表的所有排列。当你完成所有元素后,你就完成了。

当然,查找包含一个元素的列表的所有排列是一项简单的任务。

因此我们得到的是这样的:

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);
}

这样做是递归地执行我上面描述的操作。 “pluckElement”函数返回列表中的元素以及该列表的 rest 。然后,“permute”函数执行在原始列表的完整排列中传入的操作(当它注意到剩余列表为空时),或者以传递的列表的每个元素递归地调用自身。

要使用该功能,您只需执行此操作:

forEachPermutation([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], check);

编辑 - 如果您只想从原始列表中提取一定数量的值,则必须修改上述值;给我一点时间。

好的 - 如果你想传入一个(例如)14个元素的列表,但是为14个中的每个不同的列表(例如)调用了“operation”函数,你可以按如下方式修改函数。外部“forEachPermutation”函数将采用额外的“len”参数,以告诉它所需字符串的长度。然后,“permute”函数将检查“partial.length”是否是该目标长度,而不是检查空余数。

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);
}

你可以用

来调用它
forEachPermutation([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], 7, check);

另一个编辑 - 如果由于某种原因你希望在“操作”功能处理每一个之后保存排列,那么我们必须考虑到这个事实正在使用的部分数组被覆盖。可以更改对“操作”的调用:

if (partial.length === len)
  operation(partial.slice(0));

它创建了“部分”数组的副本,因此每次“操作”调用都会获得它自己的数组。当然,它会让这个过程变慢。

答案 1 :(得分:2)

问题是你正在做额外的工作。在c循环中,您已经知道ab不相等,但无论如何都要检查。您可以利用c风格for循环的灵活性:

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++) {
            ...
        }
    }
}

这在内循环中有点冗长,但至少是简单的。