获取所有数组[1、1、1、1、1、1、0、0、0、0]的排列

时间:2020-05-09 23:18:00

标签: javascript arrays typescript combinations

我正在尝试创建一个脚本,该脚本生成二进制开关的所有各种排列,其中应该有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);

JS Bin

1 个答案:

答案 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();
        }
    }
}

相关问题