JavaScript 中的排列?

IT技术 javascript permutation
2021-02-02 01:16:16

我正在尝试编写一个执行以下操作的函数:

  • 将整数数组作为参数(例如 [1,2,3,4])
  • 创建一个包含 [1,2,3,4] 的所有可能排列的数组,每个排列的长度为 4

下面的函数(我在网上找到它)通过将字符串作为参数并返回该字符串的所有排列来完成此操作

我不知道如何修改它以使其适用于整数数组,(我认为这与某些方法在字符串上的工作方式与在整数上的工作方式不同有关,但我不确定。 ..)

var permArr = [], usedChars = [];
function permute(input) {
  var i, ch, chars = input.split("");
  for (i = 0; i < chars.length; i++) {
    ch = chars.splice(i, 1);
    usedChars.push(ch);
    if (chars.length == 0)
      permArr[permArr.length] = usedChars.join("");
    permute(chars.join(""));
    chars.splice(i, 0, ch);
    usedChars.pop();
  }
  return permArr
};

注意:我希望函数返回整数数组而不是字符串数组

我真的需要 JavaScript 的解决方案。我已经想出了如何在 python 中做到这一点

6个回答

有点晚了,但想在这里添加一个稍微优雅的版本。可以是任何数组...

function permutator(inputArr) {
  var results = [];

  function permute(arr, memo) {
    var cur, memo = memo || [];

    for (var i = 0; i < arr.length; i++) {
      cur = arr.splice(i, 1);
      if (arr.length === 0) {
        results.push(memo.concat(cur));
      }
      permute(arr.slice(), memo.concat(cur));
      arr.splice(i, 0, cur[0]);
    }

    return results;
  }

  return permute(inputArr);
}

添加 ES6 (2015) 版本。也不会改变原始输入数组。在 Chrome 的控制台中工作...

const permutator = (inputArr) => {
  let result = [];

  const permute = (arr, m = []) => {
    if (arr.length === 0) {
      result.push(m)
    } else {
      for (let i = 0; i < arr.length; i++) {
        let curr = arr.slice();
        let next = curr.splice(i, 1);
        permute(curr.slice(), m.concat(next))
     }
   }
 }

 permute(inputArr)

 return result;
}

所以...

permutator(['c','a','t']);

产量...

[ [ 'c', 'a', 't' ],
  [ 'c', 't', 'a' ],
  [ 'a', 'c', 't' ],
  [ 'a', 't', 'c' ],
  [ 't', 'c', 'a' ],
  [ 't', 'a', 'c' ] ]

和...

permutator([1,2,3]);

产量...

[ [ 1, 2, 3 ],
  [ 1, 3, 2 ],
  [ 2, 1, 3 ],
  [ 2, 3, 1 ],
  [ 3, 1, 2 ],
  [ 3, 2, 1 ] ]
我们如何设置排列的长度?
2021-03-19 01:16:16
线有var cur, memo = memo || [];什么作用?
2021-03-22 01:16:16
如果您有一个方便的阶乘函数(考虑到您很可能正在处理排列),您可以通过将外部作用域初始化更改为 来加速它 var results = new Array(factorial(inputArr.length)), length=0,然后替换results.push(…)results[length++]=…
2021-03-30 01:16:16
@user2965967 声明了cur 和memo,并且将memo 初始化为memo 的值,除非它是falsey(包括undefined),在这种情况下它将是一个空数组。换句话说,为函数参数提供默认值是一种不太理想的方式。
2021-04-02 01:16:16
slice()permute(curr.slice(), m.concat(next))真的有必要吗?
2021-04-03 01:16:16

如果您注意到,代码实际上在进行任何排列之前将字符拆分为一个数组,因此您只需删除连接和拆分操作

var permArr = [],
  usedChars = [];

function permute(input) {
  var i, ch;
  for (i = 0; i < input.length; i++) {
    ch = input.splice(i, 1)[0];
    usedChars.push(ch);
    if (input.length == 0) {
      permArr.push(usedChars.slice());
    }
    permute(input);
    input.splice(i, 0, ch);
    usedChars.pop();
  }
  return permArr
};


document.write(JSON.stringify(permute([5, 3, 7, 1])));

2021-03-12 01:16:16
@思干腾。我在尝试使用您的功能时发生了一些奇怪的事情。我把它保存在一个 .js 中,在那里我有我所有的“列表操作功能”。如果我将它与 permute([1,2,3]) 和后来的 permute([4,5,6]) 一起使用,则后者的输出仍然具有结果,即第一个的输出。知道如何解决这个问题吗?非常感谢 !
2021-03-28 01:16:16
在你的函数中访问全局变量,错误的形式!
2021-04-05 01:16:16

以下非常有效的算法使用Heap 的方法生成运行时复杂度为 O(N!) 的 N 个元素的所有排列:

function permute(permutation) {
  var length = permutation.length,
      result = [permutation.slice()],
      c = new Array(length).fill(0),
      i = 1, k, p;

  while (i < length) {
    if (c[i] < i) {
      k = i % 2 && c[i];
      p = permutation[i];
      permutation[i] = permutation[k];
      permutation[k] = p;
      ++c[i];
      i = 1;
      result.push(permutation.slice());
    } else {
      c[i] = 0;
      ++i;
    }
  }
  return result;
}

console.log(permute([1, 2, 3]));

作为空间复杂度为 O(N)生成器实现的相同算法

性能对比

随意将您的实现添加到以下benchmark.js测试套件中:

Chrome 48 的运行时结果:

如何更改此代码以提供固定 n = 2 的结果?例如,假设我们有一组三个字母:A、B 和 C。我们可能会问有多少种方法可以排列该组中的 2 个字母。每个可能的排列都是排列的一个例子。可能排列的完整列表是:AB、AC、BA、BC、CA 和 CB。
2021-03-15 01:16:16
@a4xrbj1 我将使用与上面给出的链接类似的策略(另请参阅stackoverflow.com/questions/127704/...计算固定长度 n(例如 AB、AC、BC for n = 2)的所有组合,然后计算每个组合使用堆的方法计算它的所有排列。当然可以优化特殊情况,例如 n = 2。
2021-03-21 01:16:16
@le_m 是的,特别是使用这个(堆的)方法,因为它太快了
2021-03-26 01:16:16
@a4xrbj1 参见例如这个问题中的代码示例: stackoverflow.com/questions/37892738/... - 或者你是专门询问修改这个(堆的)方法?
2021-04-01 01:16:16
生成器版本无法正常工作,yield permutation.slice()如果您不切片,您应该这样做,您只会计算最后一个排列。
2021-04-03 01:16:16
var inputArray = [1, 2, 3];

var result = inputArray.reduce(function permute(res, item, key, arr) {
    return res.concat(arr.length > 1 && arr.slice(0, key).concat(arr.slice(key + 1)).reduce(permute, []).map(function(perm) { return [item].concat(perm); }) || item);
}, []);


alert(JSON.stringify(result));
是否有与该概念平行的 lisp[1,2,3].length == 3 && "foo" || "bar"[1,2].length == 3 && "foo" || "bar"哦,天哪!有!(or (and (= 3 2) (print "hello!")) (print "goodbye"))
2021-03-10 01:16:16
哇,尽管它简洁且缺乏文档,但我认为这是最优雅的答案。我对这个算法的解释是:对于数组中的每个项目(减少),选择所有其他项目,排列它们(递归),并连接到这个项目。
2021-03-19 01:16:16
@lolmaus-AndreyMikhaylov 如何删除重复项,如果可以,请更新答案
2021-03-19 01:16:16
在此处尝试了此解决方案:codewars.com/kata/reviews/5254ca2719453dcc0b000280/groups/...我已将原始高尔夫代码展开为可读的代码,但本质上是相同的。它的问题在于它会产生重复项,我不得不对结果做额外的.filter(uniq)处理。
2021-03-25 01:16:16
如果它甚至不可读,它怎么能优雅。这是一个简单的修复,我建议进行编辑。
2021-03-26 01:16:16

我改进了司干腾回答

现在可以permute多次调用,因为permArrusedChars每次都被清除。

function permute(input) {
    var permArr = [],
        usedChars = [];
    return (function main() {
        for (var i = 0; i < input.length; i++) {
            var ch = input.splice(i, 1)[0];
            usedChars.push(ch);
            if (input.length == 0) {
                permArr.push(usedChars.slice());
            }
            main();
            input.splice(i, 0, ch);
            usedChars.pop();
        }
        return permArr;
    })();
}