我正在尝试创建一个脚本,该脚本生成二进制开关的所有各种排列,其中应该有5个1
和4个0
。并且数组的大小应为9
。
我尝试了以下代码。排列的条件是:
1.数组集应该是唯一的。
2.相邻的1
不能超过3个
const row = [1, 1, 1, 1, 1, 0, 0, 0, 0];
const list = [];
const fullList = [];
// To make sure that no more than 3 `1` are next to each other
const isRowValid = (row) => {
let isValid = true;
for(let i = 0; i+2 < row.length; i++) {
if(row[i] === 1 && row[i+1] === 1 && row[i+2] === 1) {
isValid = false;
break;
}
}
return isValid;
}
const combinations = (row, baseIndex, currentIndex, iterationLevel, list) => {
if(currentIndex > row.length - iterationLevel) {
baseIndex++;
currentIndex = 0;
}
if(baseIndex + iterationLevel > row.length) {
baseIndex = 0;
iterationLevel++;
}
if(iterationLevel === 5) {
return;
}
let rowCopy = [...row]
if(baseIndex > currentIndex ) {
let first = [...row.slice(0, currentIndex)];
let second = [...row.slice(currentIndex)];
let value = second.splice(baseIndex - currentIndex, iterationLevel);
rowCopy = [...first, ...value, ...second]
} else if(baseIndex < currentIndex) {
let first = [...row.slice(0, currentIndex + iterationLevel)];
let second = [...row.slice(currentIndex + iterationLevel)];
let value = first.splice(baseIndex, iterationLevel);
rowCopy = [...first, ...value, ...second];
}
if(isRowValid(rowCopy)) {
list.push(rowCopy);
}
console.log(rowCopy);
combinations(row, baseIndex, currentIndex + 1, iterationLevel, list);
}
combinations(row, 0, 0, 1, list);
list.forEach(l => combinations(l, 0, 0, 1, fullList));
// To remove duplicates
for(let i = 0; i < fullList.length; i++) {
const base = fullList[i]
for(let j = i + 1; j < fullList.length; j++) {
const isSame = fullList[j].every((l, m) => base[m] === l);
if(isSame) {
fullList[j] = [];
}
}
}
let filtered = fullList.filter(l => l.length !== 0);
console.log(filtered.length);
filtered.slice(0, 100).map(i => console.log(i));
console.log(fullList.length);
答案 0 :(得分:3)
如果我理解正确,您的意思是排列,而不是组合,在每个排列中,打开的顺序开关不应超过3个。
无论何时必须生成排列或组合,都可以使用递归回溯算法。
这个想法很简单,在每个步骤中,您都遵循可能的选择,直到满足基本条件为止(例如,由于perm.length === switchCount
,排列完成了)。采取措施时,您会在问题的状态上反映出该选择,而当递归调用返回时,您将撤消这些影响。
为了确定在每个步骤中可以做出哪些选择,我们需要跟踪问题的状态。在这里,我们只需要知道还剩下多少个开/关开关以及到目前为止有多少个顺序开关(seqOn
)。
const perms = permute(5, 4);
console.log(perms.length);
console.log(perms);
function permute(on, off) {
const switchCount = on + off;
const perm = [], perms = [];
p(on, off, 0);
return perms;
function p(on, off, seqOn) {
if (perm.length === switchCount) {
perms.push([...perm]);
return;
}
if (on && seqOn < 3) {
perm.push(1);
p(on - 1, off, seqOn + 1);
perm.pop();
}
if (off) {
perm.push(0);
p(on, off - 1, 0);
perm.pop();
}
}
}
如果我们有许多要枚举的排列,我们也可以使用生成器来节省内存。在这里,我产生了相同的perm
数组,该数组节省了O(n)时间副本。只要您不需要保留副本,只需枚举开关就可以了。
for (const perm of permute(5, 4)) {
console.log(perm);
}
function* permute(on, off) {
const switchCount = on + off;
const perm = [];
yield* p(on, off, 0);
function* p(on, off, seqOn) {
if (perm.length === switchCount) {
yield perm;
return;
}
if (on && seqOn < 3) {
perm.push(1);
yield* p(on - 1, off, seqOn + 1);
perm.pop();
}
if (off) {
perm.push(0);
yield* p(on, off - 1, 0);
perm.pop();
}
}
}