假设我们有元素0和1,它们可以多次遮盖,例如00 00 11
,00 00 11 11
或01 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
开始具有1
,2
和00 11 22
的基数。
它们被认为是相同的,因为相等的数字位于相同的位置。只需切换两个数字(例如0与1),您就会得到另一个。
同样,所有这些示例都被分成两组,以提高可读性。 00 11 22
的含义与001122
相同,其中每个数字都是输入数组中的单个元素。
是创建排列后之后筛选元素的最快方法,还是可以在函数中实现此条件?
编辑:并不需要它是置换数组的生成器函数
编辑2::为了使事情更加清楚:我给出的第一个示例具有模式abab
,其中a
可以是0
或{{ 1}}和1
相反。第二个示例遵循模式b
,其中abcabc
,a
和b
可以全部为c
,0
或1
(但不相同)。
答案 0 :(得分:1)
如果12
和21
相同,就像10
和01
一样,那么就不要首先创建它们。这里的关键思想是为每个值都具有<规范>规范形式,例如是数字升序排列。因此,您只有值00
,01
,02
,11
,12
和22
。
如果序列中的顺序也无关紧要,那么您就不应该生成排列。再次,为序列选择规范形式。
然后,您可以轻松地仅生成升序形式,并为零件和整个序列使用一次相同的生成器:
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)
在不同排列之间具有相等条件后,我们需要为每个模式(相等类)建立一个<规范>规范形式。然后,我们将尝试仅生成那些。
在您的情况下,1
,2
和3
的顺序无关紧要,我们可以决定它们各自的首次出现必须按照{{1 }}和123
不能在没有2
的情况下出现,而1
会在没有3
的情况下出现。因此,无论模式是2
,aabcbc
,aacbcb
,…还是bbacac
,我们要生成的唯一形式是ccbaba
。或者,当模式为112323
/ aaa
/ bbb
时,我们只会生成ccc
,而不会生成111
或222
。
然后我们可以编写一种遵循以下规则的规范形式的算法:
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次)