Skip to content

Instantly share code, notes, and snippets.

@sebinsua
Last active January 29, 2020 00:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sebinsua/5284e13f9f9891e7919fe72905453b5a to your computer and use it in GitHub Desktop.
Save sebinsua/5284e13f9f9891e7919fe72905453b5a to your computer and use it in GitHub Desktop.
Permutations and combinations from scratch.
function permute(arr) {
if (arr.length === 2) {
const [a, b] = arr;
return [[a, b], [b, a]];
}
let permutationItems = [];
for (let idx = 0; idx < arr.length; idx++) {
const first = arr[idx];
const tail = [...arr];
tail.splice(idx, 1);
permutationItems.push(...permute(tail).map(part => [first, ...part]));
}
return permutationItems;
}
const permutations = permute(["F", "I", "A", "D", "H"]);
for (let permutation of permutations) {
console.log(permutation.join(""));
}
@sebinsua
Copy link
Author

sebinsua commented Jan 28, 2020

What if permute was more memory efficient due to the usage of generators?

function* permute(arr) {
  if (arr.length === 2) {
    const [a, b] = arr;
    yield [a, b];
    yield [b, a];
  }

  for (let idx = 0; idx < arr.length; idx++) {
    const first = arr[idx];

    const tail = [...arr];
    tail.splice(idx, 1);

    for (let part of permute(tail)) {
      yield [first, ...part];
    }
  }
}

for (let permutation of permute(["F", "I", "A", "D", "H"])) {
  console.log(permutation.join(""));
}

@sebinsua
Copy link
Author

sebinsua commented Jan 28, 2020

What if permute handled more edge-cases?

function* permute(arr) {
  if (arr.length === 0) {
    return;
  }

  if (arr.length === 1) {
    yield [arr[0]];
    return;
  }

  if (arr.length === 2) {
    const [a, b] = arr;
    yield [a, b];
    yield [b, a];
    return;
  }

  for (let idx = 0; idx < arr.length; idx++) {
    const first = arr[idx];

    const tail = [...arr];
    tail.splice(idx, 1);

    for (let part of permute(tail)) {
      yield [first, ...part];
    }
  }
}

for (let permutation of permute(["F", "I", "A", "D", "H"])) {
  console.log(permutation.join(""));
}

@sebinsua
Copy link
Author

sebinsua commented Jan 28, 2020

What if it were possible to use permute to generate all permutations of a particular length?

function* permute(arr, _length = arr.length) {
  const length = Math.min(_length, arr.length);

  if (arr.length === 0) {
    return;
  }

  if (arr.length === 1) {
    yield [arr[0]];
    return;
  }

  if (arr.length === 2) {
    const [a, b] = arr;
    yield [a, b];
    yield [b, a];
    return;
  }

  let seen = new Set();

  for (let idx = 0; idx < arr.length; idx++) {
    const first = arr[idx];

    const tail = [...arr];
    tail.splice(idx, 1);

    for (let part of permute(tail, length - 1)) {
      const permutation = [first, ...part].slice(0, length);
      const key = permutation.join("");

      if (seen.has(key)) {
        continue;
      }

      seen.add(key);
      yield permutation;
    }
  }
}

let count = 0;
for (let permutation of permute(["A", "B", "C", "D", "E", "F", "G"], 3)) {
  console.log(permutation.join(""));
  count++;
}

console.log('Count:', count);

@sebinsua
Copy link
Author

sebinsua commented Jan 29, 2020

What if it were possible to generate distinct combinations that do not care about the ordering?

const defaultKey = arr => arr.join("");

function createPermute(toKey = defaultKey) {
  return function* permute(arr, _length = arr.length) {
    const length = Math.min(_length, arr.length);

    if (arr.length === 0) {
      return;
    }

    if (arr.length === 1) {
      yield [arr[0]];
      return;
    }

    if (arr.length === 2) {
      const [a, b] = arr;
      yield [a, b];
      yield [b, a];
      return;
    }

    let seen = new Set();

    for (let idx = 0; idx < arr.length; idx++) {
      const first = arr[idx];

      const tail = [...arr];
      tail.splice(idx, 1);

      for (let part of permute(tail, length - 1)) {
        const permutation = [first, ...part].slice(0, length);
        const key = toKey(permutation);

        if (seen.has(key)) {
          continue;
        }

        seen.add(key);
        yield permutation;
      }
    }
  };
}

const combine = createPermute(arr =>
  [...arr].sort((a, b) => a.localeCompare(b)).join("")
);

let count = 0;
for (let combination of combine(["A", "B", "C", "D", "E", "F", "G"], 5)) {
  console.log(combination.join(""));

  count++;
}

console.log("Count:", count);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment