【问题标题】:Quick and efficient permutations with max length最大长度的快速高效排列
【发布时间】:2019-09-19 17:05:10
【问题描述】:

我想在 javascript 中创建一个排列函数,它可以非常快速地找到数组的所有排列,以及它之前的每个长度(我将给出一个进一步解释的示例)。它需要尽可能快速和高效,并且不应该有任何重复。例如,该函数将输入一个类似于

的列表
[0, 1, 2]

预期的输出是

[[0], [1], [2], [0, 1], [0, 2], [1, 2], [1, 2, 3]]

数组中不会有任何重复项(例如 [0, 0] 不起作用),并且交换的值也不应该存在([1, 0] 和 [0, 1] 都不会'不在那里。只有其中一个)。

我查看了this questionthis question,但仍然无法得到我想要的。

最重要的是,这需要尽可能高效(尽可能缩短执行时间)

【问题讨论】:

  • @PatrickRoberts 我已经尝试了我发布的两个链接。
  • but am still unable to get what I wanted。在您的案例中,他们未能解决什么问题?要具体和详细。您的问题普遍缺乏明显的努力,因为您没有提出任何具体要求。 “尽可能高效”并不具体。您试图满足的具体时间限制是什么?除此之外就是浪费在优化上。
  • @PatrickRoberts 太慢了。我必须运行它的次数,然后检查结果以使其成为我想要的那样花费了太长时间
  • 根据您的评论和您的问题,我唯一能做出的假设是您说生成[[0], [1], [2], [0, 1], [0, 2], [1, 2], [1, 2, 3]] 太慢了。我觉得这很难相信。
  • @PatrickRoberts 我做了一个较小的例子,如果有 6 位数字,则需要更长的时间。如果有更多,则需要更长的时间。等等。

标签: javascript permutation


【解决方案1】:

词汇课:这些实际上是组合,而不是排列。对于排列,不同的顺序是不同的排列。此外,从技术上讲,您想要的并不是所有组合,因为有一个空组合 [],而且您似乎不希望包含它。

不管怎样,这是我准备的东西。

function getCombinations(array) {
    let combinations = [];
    for (let value of array) {
        combinations = combinations
            .concat(combinations.map(value0 => value0.concat([value])))
            .concat([[value]]);
    }
    
    return combinations;
}

console.log(getCombinations([0, 1, 2, 3]))

【讨论】:

  • 感谢您的回复!这正是我要找的
【解决方案2】:

根据 Mason 的回答,我想我会针对性能进行更多调整,避免使用 Array.prototype.concat() 进行复制的开销,以及使用 for...of 隐式使用 array iterator

function getCombinations (array) {
  const combinations = [];

  for (let i = 0; i < array.length; ++i) {
    const value = array[i];
    const { length } = combinations;

    combinations.push([value]);

    for (let j = 0; j < length; ++j) {
      const oldCombination = combinations[j];
      const oldLength = oldCombination.length;
      const newCombination = [];

      for (let k = 0; k < oldLength; ++k) {
        newCombination.push(oldCombination[k]);
      }

      newCombination.push(value);
      combinations.push(newCombination);
    }
  }

  return combinations;
}

const input = Array.from({ length: 23 }, (_, i) => i);
const start = performance.now();
getCombinations(input);
console.log(performance.now() - start);

下面,我提供了一个基准来与 Mason 的答案进行比较:

function getCombinationsMason(array) {
  let combinations = [];
  for (let value of array) {
    combinations = combinations
      .concat(combinations.map(value0 => value0.concat([value])))
      .concat([
        [value]
      ]);
  }

  return combinations;
}

function getCombinationsPatrick(array) {
  const combinations = [];

  for (let i = 0; i < array.length; ++i) {
    const value = array[i];
    const {
      length
    } = combinations;

    combinations.push([value]);

    for (let j = 0; j < length; ++j) {
      const oldCombination = combinations[j];
      const oldLength = oldCombination.length;
      const newCombination = [];

      for (let k = 0; k < oldLength; ++k) {
        newCombination.push(oldCombination[k]);
      }

      newCombination.push(value);
      combinations.push(newCombination);
    }
  }

  return combinations;
}

function average(algorithm, input, times) {
  let total = 0;

  for (let i = 0; i < times; ++i) {
    total -= performance.now();
    algorithm(input);
    total += performance.now();
  }

  return `${algorithm.name}(): ${(total / times).toFixed(2)}ms average over ${times} times`;
}

const input = Array.from({
  length: 18
}, (_, i) => i);

console.log('input.length:', input.length);

// randomize order of testing
if (Math.random() > 0.5) {
  // warmup
  average(getCombinationsMason, input, 2);
  average(getCombinationsPatrick, input, 2);
  // benchmark
  console.log(average(getCombinationsMason, input, 20));
  console.log(average(getCombinationsPatrick, input, 20));
} else {
  // warmup
  average(getCombinationsPatrick, input, 2);
  average(getCombinationsMason, input, 2);
  // benchmark
  console.log(average(getCombinationsPatrick, input, 20));
  console.log(average(getCombinationsMason, input, 20));
}

【讨论】:

  • 感谢您的回复。梅森实际上更快,所以我会接受他的回答。但你的也可以,而且比我之前使用的要好得多。
  • @AniketGupta 立即尝试
  • 我不知道for...of 的性能差很多,感谢您提供信息
  • @Mason 可能不会长久;在某些情况下,优化器可以完全绕过所有迭代器函数调用的开销,并接近普通 for 循环的性能,但并不总是能够预测这些优化何时开始。
猜你喜欢
  • 2016-04-30
  • 1970-01-01
  • 1970-01-01
  • 2011-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-09
  • 2022-10-05
相关资源
最近更新 更多