【问题标题】:Nested for loops - how to ignore certain combinations?嵌套 for 循环 - 如何忽略某些组合?
【发布时间】:2010-12-14 03:05:08
【问题描述】:

我正在做某种蛮力攻击来解决问题。理论上这是一个可行的解决方案,实际上也是,但需要相当长的时间。

我有 7 个相互嵌套的 for 循环,但我只需要没有重复的“for 变量”的组合。所以例如允许使用 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 循环?

谢谢。

【问题讨论】:

  • 出于某种原因,我在想 B+ 树。 en.wikipedia.org/wiki/B%2B_tree 但不要问我怎么做。我只记得“管道”和“扇出/扇入”处理
  • 您是否可以将验证结果(例如基于 id)存储在某处?如果可能的话,您可以检查一次并存储该组合是否有效。

标签: javascript


【解决方案1】:

您在这里所做的是遍历 14 个离散值列表的所有排列

一般来说,要访问一个谨慎事物列表的所有排列,您需要访问列表中的每个元素。对于每个元素,形成一个新列表,其中包含原始列表的所有 other 元素。获取 that 列表的所有排列,将元素添加到每个列表中,然后您就获得了原始列表中可以从该特定元素开始的所有排列。完成所有元素后,就完成了。

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

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

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

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

好的 - 如果你想传入一个(比如说)14 个元素的列表,但是为 14 个中的(比如说)7 个不同的列表调用“操作”函数,你可以按如下方式修改函数。外部“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));

这会复制“部分”数组,以便每次调用“操作”都可以使用自己的数组。当然,这会使过程变慢。

【讨论】:

  • 我认为这很接近,但并不完全正确。他不想要集合的所有排列。他想要该集合的所有七元素组合的所有排列。
  • 虽然它破坏了上述代码的一般用途,但我认为您可以通过将 (remainder.length === 0) 条件替换为 (remainder.length === 7) 来实现这一点,其中7 是原始数组的长度减去结果数组所需的基数。
  • 这个解决方案的空间复杂度是多少?
  • 空间复杂度——嗯,堆栈上的调用次数永远不会超过“len”。如果您要对数千个元素的列表进行排列,那可能是个问题,但这样的工作无论如何都会花很长时间:-)
  • 通过避免这么多新的数组结构,它可能会变得更好一些。 “部分”数组实际上可能只是一个被传递的数组。
【解决方案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++) {
            ...
        }
    }
}

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

【讨论】:

  • ... 概括起来可能有点混乱
  • 很好地发现,你完全正确。但是,这会变得相当混乱,并且确实无法推广。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-15
  • 2019-05-11
  • 2021-11-11
  • 1970-01-01
  • 1970-01-01
  • 2020-07-06
相关资源
最近更新 更多