生成 JavaScript 数组的排列

IT技术 javascript arrays recursion permutation
2021-02-11 11:35:32

我在javascript中有n个不同元素的数组,我知道有n个!对这些元素进行排序的可能方法。我想知道生成该数组所有可能排序的最有效(最快)算法是什么?

我有这个代码:

var swap = function(array, frstElm, scndElm) {

    var temp = array[frstElm];
    array[frstElm] = array[scndElm];
    array[scndElm] = temp;
}

var permutation = function(array, leftIndex, size) {

    var x;

    if(leftIndex === size) {

        temp = "";

        for (var i = 0; i < array.length; i++) {
            temp += array[i] + " ";
        }

        console.log("---------------> " + temp);

    } else {

        for(x = leftIndex; x < size; x++) {
            swap(array, leftIndex, x);
            permutation(array, leftIndex + 1, size);
            swap(array, leftIndex, x);
        }
    }
}

arrCities = ["Sidney", "Melbourne", "Queenstown"];
permutation(arrCities, 0, arrCities.length);

它有效,但我想交换每个项目以获得组合在内存方面有点昂贵,我认为这样做的一个好方法是只关注数组的索引并获取数字的所有排列,我是想知道是否有一种方法可以计算所有这些而无需切换数组中的元素?我想递归地获得所有这些是可能的,我需要帮助来做到这一点。

例如,如果我有:

arrCities = ["Sidney", "Melbourne", "Queenstown"];

我希望输出是:

[[012],[021],[102],[120],[201],[210]]

或者:

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

我正在阅读:http : //en.wikipedia.org/wiki/Permutation#Algorithms_to_generate_permutations

但维基百科从来不擅长解释。我不太明白,我不得不说我的数学水平不是最好的。

5个回答

这个函数perm(xs)返回给定数组的所有排列:

function perm(xs) {
  let ret = [];

  for (let i = 0; i < xs.length; i = i + 1) {
    let rest = perm(xs.slice(0, i).concat(xs.slice(i + 1)));

    if(!rest.length) {
      ret.push([xs[i]])
    } else {
      for(let j = 0; j < rest.length; j = j + 1) {
        ret.push([xs[i]].concat(rest[j]))
      }
    }
  }
  return ret;
}

console.log(perm([1,2,3]).join("\n"));

使用堆的方法(您可以维基百科文章链接到的这篇论文中找到它),您可以生成 N 个元素的所有排列,运行时复杂度为 O(N!),空间复杂度为 O(N)。该算法基于交换元素。AFAIK 这是尽可能快的,没有更快的方法来计算所有排列。

有关实现和示例,请查看我最近在相关问题“javascript 中的排列”中的回答

使用此答案(stackoverflow.com/a/37580979/1647737)进行的基本测试比上面的@chacmoolvm 答案快 10 倍
2021-03-27 11:35:32
@DSB 我想我不明白。如果您不想生成所有排列而只生成一些排列,请查看Lehmer 代码,它允许您根据字典排列索引快速计算单个排列。
2021-04-12 11:35:32
一些方法 tath 不会计算所有可能的排列,但会进行非常接近的计算?
2021-04-14 11:35:32

这只是为了好玩 - 我在一个字符串中递归解决

const perm = a => a.length ? a.reduce((r, v, i) => [ ...r, ...perm([ ...a.slice(0, i), ...a.slice(i + 1) ]).map(x => [ v, ...x ])], []) : [[]]
好吧,我现在只想说,我喜欢单线。非常感谢你做的这些!
2021-03-26 11:35:32
计算 11 个元素的排列需要很长时间。
2021-03-30 11:35:32

这是我基于le_m代码的版本:

function permute(array) {
	Array.prototype.swap = function (index, otherIndex) {
		var valueAtIndex = this[index]

		this[index] = this[otherIndex]
		this[otherIndex] = valueAtIndex
	}

	var result = [array.slice()]

	, length = array.length

	for (var i = 1, heap = new Array(length).fill(0)
		; i < length
	;)
		if (heap[i] < i) {
			array.swap(i, i % 2 && heap[i])
			result.push(array.slice())
			heap[i]++
			i = 1
		} else {
			heap[i] = 0
			i++
		}

	return result
}

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

这是我对相同算法的递归 JavaScript 实现:

Array.prototype.swap = function (index, otherIndex) {
	var valueAtIndex = this[index]

	this[index] = this[otherIndex]
	this[otherIndex] = valueAtIndex
}

Array.prototype.permutation = function permutation(array, n) {
	array = array || this
	n = n || array.length

	var result = []

	if (n == 1)
		result = [array.slice()]
	else {
		const nextN = n - 1

		for (var i = 0; i < nextN; i++) {
			result.push(...permutation(array, nextN))
			array.swap(Number(!(n % 2)) && i, nextN)
		}

		result.push(...permutation(array, nextN))
	}

	return result
}

console.log([1, 2, 3].permutation())

function permutations(str) {
 return (str.length <= 1) ? [str] :
         Array.from(new Set(
           str.split('')
              .map((char, i) => permutations(str.substr(0, i) + str.substr(i + 1)).map(p => char + p))
              .reduce((r, x) => r.concat(x), [])
         ));
}