diff --git a/package.json b/package.json index 38c77ae..5cf568f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@combinatorics/n-combinations", "description": "Set n-combinations for JavaScript", - "version": "0.0.1", + "version": "0.0.2", "license": "AGPL-3.0", "author": "make-github-pseudonymous-again", "homepage": "https://computational-combinatorics.github.io/n-combinations", diff --git a/src/_combinations.js b/src/_combinations.js new file mode 100644 index 0000000..88b87df --- /dev/null +++ b/src/_combinations.js @@ -0,0 +1,38 @@ +import {list} from '@iterable-iterator/list'; +import {range} from '@iterable-iterator/range'; + +/** + * Yields all k-subsets of {0, 1, ..., n-1}. + * + * @param {number} n + * @param {number} k + * @returns {IterableIterator} + */ +export default function* _combinations(n, k) { + if (k > n) return; + + const indices = list(range(0, k, 1)); + + yield indices; + + while (true) { + let i = k - 1; + + // eslint-disable-next-line no-constant-condition + while (true) { + if (i < 0) return; + + if (indices[i] !== i + n - k) { + let pivot = ++indices[i]; + + for (++i; i < k; ++i) indices[i] = ++pivot; + + break; + } + + --i; + } + + yield indices; + } +} diff --git a/src/combinations.js b/src/combinations.js index 05e2615..5d85e5e 100644 --- a/src/combinations.js +++ b/src/combinations.js @@ -1,6 +1,7 @@ import {list} from '@iterable-iterator/list'; -import {pick} from '@iterable-iterator/map'; -import {range} from '@iterable-iterator/range'; +import {map, pick} from '@iterable-iterator/map'; + +import _combinations from './_combinations.js'; /** * Yields all combinations of each possible choice of r elements @@ -16,42 +17,14 @@ import {range} from '@iterable-iterator/range'; * * @param {Iterable} iterable - The input iterable. * @param {number} r - The size of the combinations to generate. - * @returns {IterableIterator} + * @returns {IterableIterator} */ -export default function* combinations(iterable, r) { +const combinations = (iterable, r) => { const pool = list(iterable); - const length = pool.length; - - if (r > length) { - return; - } - - const indices = list(range(0, r, 1)); - - yield list(pick(pool, indices)); - - while (true) { - let i = r - 1; - - // eslint-disable-next-line no-constant-condition - while (true) { - if (i < 0) { - return; - } - - if (indices[i] !== i + length - r) { - let pivot = ++indices[i]; - - for (++i; i < r; ++i) { - indices[i] = ++pivot; - } - - break; - } - - --i; - } + return map( + (indices) => list(pick(pool, indices)), + _combinations(pool.length, r), + ); +}; - yield list(pick(pool, indices)); - } -} +export default combinations;