JavaScript中带有重复元素的独特排列

时间:2019-06-19 16:52:29

标签: javascript math permutation

假设我们有元素0和1,它们可以多次遮盖,例如00 00 1100 00 11 1101 11(分成2组,以提高可读性)。 / p>

我已经具有生成所有唯一排列的功能:

class UniqueElement {
  constructor(value, occurrences) {
    this.value = value;
    this.occurrences = occurrences;
  }
}

function permUnique(elements) {
  let set = new Set(elements);
  let listunique = Array.from(set).map((i) => new UniqueElement(i, elements.filter((el) => el === i).length));
  let u = elements.length;
  return permUniqueHelper(listunique, "0".repeat(u).split("").map((i) => parseInt(i)), u - 1);
}
function* permUniqueHelper(listunique, result_list, d) {
  if (d < 0) {
    yield [...result_list];
  } else {
    for (const i of listunique) {
      if (i.occurrences > 0) {
        result_list[d] = i.value;
        i.occurrences--;
        for (const g of permUniqueHelper(listunique, result_list, d - 1)) yield g;
        i.occurrences++;
      }
    }
  }
}

// call like:
// permUnique([0,0,1,1])
console.log(Array.from(permUnique([0,0,1,1])).join('\n'));

从此处翻译成JavaScript:https://stackoverflow.com/a/6285203/10362619

现在我的意图是将这些生成的排列用作其他对象的索引。 然后在代码中使用这些对象,这些对象的顺序无关紧要:

10 10
01 01
例如

到底是相同的,还是

01 20 12
02 10 21
10 21 02
12 01 20
...

0开始具有1200 11 22的基数。

它们被认为是相同的,因为相等的数字位于相同的位置。只需切换两个数字(例如0与1),您就会得到另一个。

同样,所有这些示例都被分成两组,以提高可读性。 00 11 22的含义与001122相同,其中每个数字都是输入数组中的单个元素。

是创建排列后之后筛选元素的最快方法,还是可以在函数中实现此条件?

编辑:并不需要它是置换数组的生成器函数

编辑2::为了使事情更加清楚:我给出的第一个示例具有模式abab,其中a可以是0或{{ 1}}和1相反。第二个示例遵循模式b,其中abcabcab可以全部为c01 (但不相同)。

3 个答案:

答案 0 :(得分:1)

如果1221相同,就像1001一样,那么就不要首先创建它们。这里的关键思想是为每个值都具有<规范>规范形式,例如是数字升序排列。因此,您只有值000102111222

如果序列中的顺序也无关紧要,那么您就不应该生成排列。再次,为序列选择规范形式。

然后,您可以轻松地仅生成升序形式,并为零件和整个序列使用一次相同的生成器:

function* generate(values, len) {
    if (len == 0)
        yield [];
    else
        for (const [i, val] of values.entries())
            for (const rest of generate(values.slice(i), len-1))
                yield [val, ...rest];
}

const duos = Array.from(generate([0, 1, 2], 2), x => x.join(""));
for (const x of generate(duos, 3))
    console.log(x.join(" "))

答案 1 :(得分:1)

在不同排列之间具有相等条件后,我们需要为每个模式(相等类)建立一个<规范>规范形式。然后,我们将尝试仅生成那些。

在您的情况下,123的顺序无关紧要,我们可以决定它们各自的首次出现必须按照{{1 }}和123不能在没有2的情况下出现,而1会在没有3的情况下出现。因此,无论模式是2aabcbcaacbcb,…还是bbacac,我们要生成的唯一形式是ccbaba。或者,当模式为112323 / aaa / bbb时,我们只会生成ccc,而不会生成111222

然后我们可以编写一种遵循以下规则的规范形式的算法:

333

这只是从不同的function patterns(values, len) { function* recurse(list, used, depth) { if (depth == len) { yield list.slice(); } else { for (let j=0; j<used; j++) { // only put already used values at this position list[depth] = values[j]; yield* recurse(list, used, depth+1); } if (used < values.length) { // or the single next unused value list[depth] = values[used]; yield* recurse(list, used+1, depth+1); // which is counted as used here } } } return recurse([], 0, 0); } 生成一定长度的所有组合,但是您可以在算法中使用相同的方法,根据一定数量的非唯一值生成置换。但是,它确实变得更加复杂:如果我们只有可用的值values,则无法实现规范形式121,但仍应生成模式122。我们需要调整规则,以使排序要求仅在出现次数相同的元素之间。因此,我们必须为它们保留不同的212个计数。

used

答案 2 :(得分:0)

我现在已按照问题注释中的@PatrickRoberts将生成器函数包装在过滤器生成器函数中。

class UniqueElement {
	constructor(value, occurrences) {
		this.value = value;
		this.occurrences = occurrences;
	}
}

function uniquePermutations(elements) {
	let set = new Set(elements);
	let listunique = Array.from(set).map((i) => new UniqueElement(i, elements.filter((el) => el === i).length));
	let u = elements.length;
	return uniquePermutationsHelper(listunique, "0".repeat(u).split("").map((i) => parseInt(i)), u - 1);
}
function* uniquePermutationsHelper(listunique, result_list, d) {
	if (d < 0) {
		yield [...result_list];
	} else {
		for (const i of listunique) {
			if (i.occurrences > 0) {
				result_list[d] = i.value;
				i.occurrences--;
				for (const g of uniquePermutationsHelper(listunique, result_list, d - 1)) yield g;
				i.occurrences++;
			}
		}
	}
}
function* uniquePermutationPatterns(elements) {
	let patterns = [];
	for (const perm of uniquePermutations(elements)) {
		// contains all unique elements in the order they appear. call patternVars.indexOf(element) and use its index for the patterns array
		let patternVars = Array.from(new Set(perm));

		let pattern = perm.map((p) => patternVars.indexOf(p));
    // convert the pattern to a number to avoid comparing array references and use the faster number comparison than the slower string comparison
		pattern = parseInt(pattern.join(""), patternVars.length);
		if (!patterns.length || !patterns.includes(pattern)) {
			patterns.push(pattern);
			yield perm;
		}
	}
}

let elements = [0,0,1,1];
let all = [...uniquePermutations(elements)];
let filtered = [...uniquePermutationPatterns(elements)];

console.log(`all permutations: (${all.length})`);
console.log(all.map((i) => i.join("")).join("\n"));
console.log(`\nfiltered permutations: (${filtered.length})`);
console.log(filtered.map((i) => i.join("")).join("\n"));

但这比仅生成所有排列慢大约3.2倍。就我而言,这无关紧要。我仅测试了元素[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2]数组的性能,并且仅测试了几次(少于10次)