boB Rudis
3 years ago
13 changed files with 1420 additions and 1 deletions
@ -1,3 +1,3 @@ |
|||||
# 2020 Advent of Code |
# 2020 Advent of Code |
||||
|
|
||||
Solutions in R, Swift, Python (in the `R` dir since I'm using RStudio's Python REPL) and mebbe others |
Solutions in R, Swift, javascript, Python (in the `R` dir since I'm using RStudio's Python REPL) and mebbe others |
||||
|
@ -0,0 +1,28 @@ |
|||||
|
// See R or Python code for the problems
|
||||
|
|
||||
|
// 01-01
|
||||
|
|
||||
|
var fs = require("fs") |
||||
|
var combn = require('generatorics') // npm install generatorics
|
||||
|
|
||||
|
input = fs.readFileSync("../input/01-01.txt", "utf-8") |
||||
|
.split("\n") |
||||
|
.map(Number) |
||||
|
|
||||
|
for (var pair of combn.combination(input, 2)) { |
||||
|
res = pair.reduce((a, b) => a+b) |
||||
|
if (res == 2020) { |
||||
|
console.log(pair.reduce((a, b) => a*b)) |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 01-02
|
||||
|
|
||||
|
for (var pair of combn.combination(input, 3)) { |
||||
|
res = pair.reduce((a, b) => a+b) |
||||
|
if (res == 2020) { |
||||
|
console.log(pair.reduce((a, b) => a*b)) |
||||
|
break |
||||
|
} |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
{ |
||||
|
"name": "js", |
||||
|
"lockfileVersion": 2, |
||||
|
"requires": true, |
||||
|
"packages": { |
||||
|
"node_modules/generatorics": { |
||||
|
"version": "1.1.0", |
||||
|
"resolved": "https://registry.npmjs.org/generatorics/-/generatorics-1.1.0.tgz", |
||||
|
"integrity": "sha1-aVBgu42IuQmzAXGlyz1CR2hmETg=", |
||||
|
"engines": { |
||||
|
"node": ">=6.0.0" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
{ |
||||
|
"env": { |
||||
|
"browser": true, |
||||
|
"es6": true, |
||||
|
"node": true |
||||
|
}, |
||||
|
"extends": "eslint:recommended", |
||||
|
"rules": { |
||||
|
"indent": [ |
||||
|
"error", |
||||
|
2 |
||||
|
], |
||||
|
"linebreak-style": [ |
||||
|
"error", |
||||
|
"unix" |
||||
|
], |
||||
|
"quotes": [ |
||||
|
"error", |
||||
|
"single" |
||||
|
], |
||||
|
"semi": [ |
||||
|
"error", |
||||
|
"never" |
||||
|
] |
||||
|
} |
||||
|
} |
@ -0,0 +1,388 @@ |
|||||
|
# Generatorics |
||||
|
|
||||
|
### An efficient combinatorics library for JavaScript utilizing ES2015 generators. Generate combinations, permutations, and power sets of arrays or strings. |
||||
|
|
||||
|
- Node |
||||
|
``` |
||||
|
npm install generatorics |
||||
|
``` |
||||
|
```javascript |
||||
|
var G = require('generatorics'); |
||||
|
``` |
||||
|
|
||||
|
- Browser |
||||
|
``` |
||||
|
bower install generatorics |
||||
|
``` |
||||
|
```html |
||||
|
<script src="file/path/to/generatorics.js"></script> |
||||
|
``` |
||||
|
|
||||
|
**Note:** This module is not transpiled for compatibility, as it degrades the performance. Check your browser/node version. |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
### power set |
||||
|
```javascript |
||||
|
for (var subset of G.powerSet(['a', 'b', 'c'])) { |
||||
|
console.log(subset); |
||||
|
} |
||||
|
// [ ] |
||||
|
// [ 'a' ] |
||||
|
// [ 'a', 'b' ] |
||||
|
// [ 'a', 'b', 'c' ] |
||||
|
// [ 'a', 'c' ] |
||||
|
// [ 'b' ] |
||||
|
// [ 'b', 'c' ] |
||||
|
// [ 'c' ] |
||||
|
``` |
||||
|
|
||||
|
### permutation |
||||
|
```javascript |
||||
|
for (var perm of G.permutation(['a', 'b', 'c'], 2)) { |
||||
|
console.log(perm); |
||||
|
} |
||||
|
// [ 'a', 'b' ] |
||||
|
// [ 'a', 'c' ] |
||||
|
// [ 'b', 'a' ] |
||||
|
// [ 'b', 'c' ] |
||||
|
// [ 'c', 'a' ] |
||||
|
// [ 'c', 'b' ] |
||||
|
|
||||
|
for (var perm of G.permutation(['a', 'b', 'c'])) { // assumes full length of array |
||||
|
console.log(perm); |
||||
|
} |
||||
|
// [ 'a', 'b', 'c' ] |
||||
|
// [ 'a', 'c', 'b' ] |
||||
|
// [ 'b', 'a', 'c' ] |
||||
|
// [ 'b', 'c', 'a' ] |
||||
|
// [ 'c', 'b', 'a' ] |
||||
|
// [ 'c', 'a', 'b' ] |
||||
|
``` |
||||
|
|
||||
|
### combination |
||||
|
```javascript |
||||
|
for (var comb of G.combination(['a', 'b', 'c'], 2)) { |
||||
|
console.log(comb); |
||||
|
} |
||||
|
// [ 'a', 'b' ] |
||||
|
// [ 'a', 'c' ] |
||||
|
// [ 'b', 'c' ] |
||||
|
``` |
||||
|
|
||||
|
For efficiency, each array being yielded is the same one being mutated on each iteration. **DO NOT** mutate the array. |
||||
|
```javascript |
||||
|
var combs = []; |
||||
|
for (var comb of G.combination(['a', 'b', 'c'], 2)) { |
||||
|
combs.push(comb); |
||||
|
} |
||||
|
console.log(combs); |
||||
|
// [ [ 'b', 'c' ], [ 'b', 'c' ], [ 'b', 'c' ] ] |
||||
|
``` |
||||
|
You can clone if necessary, or use the [clone submodule](#clone-submodule) |
||||
|
|
||||
|
### permutation of combination |
||||
|
```javascript |
||||
|
for (var perm of G.permutationCombination(['a', 'b', 'c'])) { |
||||
|
console.log(perm); |
||||
|
} |
||||
|
// [ ] |
||||
|
// [ 'a' ] |
||||
|
// [ 'a', 'b' ] |
||||
|
// [ 'a', 'b', 'c' ] |
||||
|
// [ 'a', 'c' ] |
||||
|
// [ 'a', 'c', 'b' ] |
||||
|
// [ 'b' ] |
||||
|
// [ 'b', 'a' ] |
||||
|
// [ 'b', 'a', 'c' ] |
||||
|
// [ 'b', 'c' ] |
||||
|
// [ 'b', 'c', 'a' ] |
||||
|
// [ 'c' ] |
||||
|
// [ 'c', 'a' ] |
||||
|
// [ 'c', 'a', 'b' ] |
||||
|
// [ 'c', 'b' ] |
||||
|
// [ 'c', 'b', 'a' ] |
||||
|
``` |
||||
|
|
||||
|
### cartesian product |
||||
|
```javascript |
||||
|
for (var prod of G.cartesian([0, 1, 2], [0, 10, 20], [0, 100, 200])) { |
||||
|
console.log(prod); |
||||
|
} |
||||
|
// [ 0, 0, 0 ], [ 0, 0, 100 ], [ 0, 0, 200 ] |
||||
|
// [ 0, 10, 0 ], [ 0, 10, 100 ], [ 0, 10, 200 ] |
||||
|
// [ 0, 20, 0 ], [ 0, 20, 100 ], [ 0, 20, 200 ] |
||||
|
// [ 1, 0, 0 ], [ 1, 0, 100 ], [ 1, 0, 200 ] |
||||
|
// [ 1, 10, 0 ], [ 1, 10, 100 ], [ 1, 10, 200 ] |
||||
|
// [ 1, 20, 0 ], [ 1, 20, 100 ], [ 1, 20, 200 ] |
||||
|
// [ 2, 0, 0 ], [ 2, 0, 100 ], [ 2, 0, 200 ] |
||||
|
// [ 2, 10, 0 ], [ 2, 10, 100 ], [ 2, 10, 200 ] |
||||
|
// [ 2, 20, 0 ], [ 2, 20, 100 ], [ 2, 20, 200 ] |
||||
|
``` |
||||
|
|
||||
|
### base N |
||||
|
```javascript |
||||
|
for (var num of G.baseN(['a', 'b', 'c'])) { |
||||
|
console.log(num); |
||||
|
} |
||||
|
// [ 'a', 'a', 'a' ], [ 'a', 'a', 'b' ], [ 'a', 'a', 'c' ] |
||||
|
// [ 'a', 'b', 'a' ], [ 'a', 'b', 'b' ], [ 'a', 'b', 'c' ] |
||||
|
// [ 'a', 'c', 'a' ], [ 'a', 'c', 'b' ], [ 'a', 'c', 'c' ] |
||||
|
// [ 'b', 'a', 'a' ], [ 'b', 'a', 'b' ], [ 'b', 'a', 'c' ] |
||||
|
// [ 'b', 'b', 'a' ], [ 'b', 'b', 'b' ], [ 'b', 'b', 'c' ] |
||||
|
// [ 'b', 'c', 'a' ], [ 'b', 'c', 'b' ], [ 'b', 'c', 'c' ] |
||||
|
// [ 'c', 'a', 'a' ], [ 'c', 'a', 'b' ], [ 'c', 'a', 'c' ] |
||||
|
// [ 'c', 'b', 'a' ], [ 'c', 'b', 'b' ], [ 'c', 'b', 'c' ] |
||||
|
// [ 'c', 'c', 'a' ], [ 'c', 'c', 'b' ], [ 'c', 'c', 'c' ] |
||||
|
``` |
||||
|
|
||||
|
## Clone Submodule |
||||
|
Each array yielded from the generator is actually the same array in memory, just mutated to have different elements. This is to avoid the unnecessary creation of a bunch of arrays, which consume memory. As a result, you get a strange result when trying to generate an array. |
||||
|
```javascript |
||||
|
var combs = G.combination(['a', 'b', 'c'], 2); |
||||
|
console.log([...combs]); |
||||
|
// [ [ 'b', 'c' ], [ 'b', 'c' ], [ 'b', 'c' ] ] |
||||
|
``` |
||||
|
Instead, you can use the clone submodule. |
||||
|
```javascript |
||||
|
var combs = G.clone.combination(['a', 'b', 'c'], 2); |
||||
|
console.log([...combs]); |
||||
|
// [ [ 'a', 'b' ], [ 'a', 'c' ], [ 'b', 'c' ] ] |
||||
|
``` |
||||
|
|
||||
|
### G.clone |
||||
|
This submodule produces generators that yield a different array on each iteration in case you need to mutate it. The [combination](#module_G.combination), [permutation](#module_G.permutation), [powerSet](#module_G.powerSet), [permutationCombination](#module_G.permutationCombination), [baseN](#module_G.baseN), [baseNAll](#module_G.baseNAll), and [cartesian](#module_G.cartesian) methods are provided on this submodule. |
||||
|
|
||||
|
## Cool things to do with ES2015 generators |
||||
|
```javascript |
||||
|
var combs = G.clone.combination([1, 2, 3], 2); |
||||
|
|
||||
|
// "for-of" loop |
||||
|
for (let comb of combs) { |
||||
|
console.log(comb); |
||||
|
} |
||||
|
|
||||
|
// generate arrays |
||||
|
Array.from(combs); |
||||
|
[...combs]; |
||||
|
|
||||
|
// generate sets |
||||
|
new Set(combs); |
||||
|
|
||||
|
// spreading in function calls |
||||
|
console.log(...combs); |
||||
|
``` |
||||
|
|
||||
|
#### Writing a code generator? Need to produce an infinite stream of minified variable names? |
||||
|
|
||||
|
No problem! Just pass in a collection of all your valid characters and start generating. |
||||
|
|
||||
|
```javascript |
||||
|
var mininym = G.baseNAll('abcdefghijklmnopqrstuvwxyz$#') |
||||
|
var name = mininym.next().value.join('') |
||||
|
global[name] = 'some value' |
||||
|
``` |
||||
|
|
||||
|
#### Card games anyone? |
||||
|
```javascript |
||||
|
var cards = [...G.clone.cartesian('♠♥♣♦', 'A23456789JQK')]; |
||||
|
console.log(G.shuffle(cards)); |
||||
|
// [ [ '♦', '6' ], [ '♠', '6' ], [ '♣', '7' ], [ '♥', 'K' ], |
||||
|
// [ '♣', 'J' ], [ '♥', '4' ], [ '♦', '2' ], [ '♥', '9' ], |
||||
|
// [ '♦', 'Q' ], [ '♠', 'Q' ], [ '♠', '4' ], [ '♠', 'K' ], |
||||
|
// [ '♥', '3' ], [ '♥', '7' ], [ '♠', '5' ], [ '♦', '7' ], |
||||
|
// [ '♥', '5' ], [ '♣', 'Q' ], [ '♣', '9' ], [ '♠', 'A' ], |
||||
|
// [ '♣', '4' ], [ '♣', '3' ], [ '♥', 'A' ], [ '♥', '8' ], |
||||
|
// [ '♣', '8' ], [ '♦', '8' ], [ '♠', '8' ], [ '♣', '5' ], |
||||
|
// [ '♥', '2' ], [ '♥', 'Q' ], [ '♦', 'A' ], [ '♥', '6' ], |
||||
|
// [ '♠', '2' ], [ '♣', '6' ], [ '♠', '3' ], [ '♦', 'K' ], |
||||
|
// [ '♦', 'J' ], [ '♠', '7' ], [ '♥', 'J' ], [ '♦', '5' ], |
||||
|
// [ '♦', '9' ], [ '♦', '3' ], [ '♠', '9' ], [ '♣', '2' ], |
||||
|
// [ '♣', 'A' ], [ '♣', 'K' ], [ '♦', '4' ], [ '♠', 'J' ] ] |
||||
|
``` |
||||
|
|
||||
|
## Documentation |
||||
|
<a name="module_G"></a> |
||||
|
|
||||
|
## G |
||||
|
|
||||
|
* [G](#module_G) |
||||
|
* [.factorial(n)](#module_G.factorial) ⇒ <code>Number</code> |
||||
|
* [.factoradic(n)](#module_G.factoradic) ⇒ <code>Array</code> |
||||
|
* [.P(n, k)](#module_G.P) ⇒ <code>Number</code> |
||||
|
* [.C(n, k)](#module_G.C) ⇒ <code>Number</code> |
||||
|
* [.choices(n, k, [options])](#module_G.choices) ⇒ <code>Number</code> |
||||
|
* [.combination(arr, [size])](#module_G.combination) ⇒ <code>Generator</code> |
||||
|
* [.permutation(arr, [size])](#module_G.permutation) ⇒ <code>Generator</code> |
||||
|
* [.powerSet(arr)](#module_G.powerSet) ⇒ <code>Generator</code> |
||||
|
* [.permutationCombination(arr)](#module_G.permutationCombination) ⇒ <code>Generator</code> |
||||
|
* [.baseN(arr, [size])](#module_G.baseN) ⇒ <code>Generator</code> |
||||
|
* [.baseNAll(arr)](#module_G.baseNAll) ⇒ <code>Generator</code> |
||||
|
* [.cartesian(...sets)](#module_G.cartesian) ⇒ <code>Generator</code> |
||||
|
* [.shuffle(arr)](#module_G.shuffle) ⇒ <code>Array</code> |
||||
|
|
||||
|
<a name="module_G.factorial"></a> |
||||
|
|
||||
|
### G.factorial(n) ⇒ <code>Number</code> |
||||
|
Calculates a factorial |
||||
|
|
||||
|
**Kind**: static method of <code>[G](#module_G)</code> |
||||
|
**Returns**: <code>Number</code> - n! |
||||
|
|
||||
|
| Param | Type | Description | |
||||
|
| --- | --- | --- | |
||||
|
| n | <code>Number</code> | The number to operate the factorial on. | |
||||
|
|
||||
|
<a name="module_G.factoradic"></a> |
||||
|
|
||||
|
### G.factoradic(n) ⇒ <code>Array</code> |
||||
|
Converts a number to the factorial number system. Digits are in least significant order. |
||||
|
|
||||
|
**Kind**: static method of <code>[G](#module_G)</code> |
||||
|
**Returns**: <code>Array</code> - digits of n in factoradic in least significant order |
||||
|
|
||||
|
| Param | Type | Description | |
||||
|
| --- | --- | --- | |
||||
|
| n | <code>Number</code> | Integer in base 10 | |
||||
|
|
||||
|
<a name="module_G.P"></a> |
||||
|
|
||||
|
### G.P(n, k) ⇒ <code>Number</code> |
||||
|
Calculates the number of possible permutations of "k" elements in a set of size "n". |
||||
|
|
||||
|
**Kind**: static method of <code>[G](#module_G)</code> |
||||
|
**Returns**: <code>Number</code> - n P k |
||||
|
|
||||
|
| Param | Type | Description | |
||||
|
| --- | --- | --- | |
||||
|
| n | <code>Number</code> | Number of elements in the set. | |
||||
|
| k | <code>Number</code> | Number of elements to choose from the set. | |
||||
|
|
||||
|
<a name="module_G.C"></a> |
||||
|
|
||||
|
### G.C(n, k) ⇒ <code>Number</code> |
||||
|
Calculates the number of possible combinations of "k" elements in a set of size "n". |
||||
|
|
||||
|
**Kind**: static method of <code>[G](#module_G)</code> |
||||
|
**Returns**: <code>Number</code> - n C k |
||||
|
|
||||
|
| Param | Type | Description | |
||||
|
| --- | --- | --- | |
||||
|
| n | <code>Number</code> | Number of elements in the set. | |
||||
|
| k | <code>Number</code> | Number of elements to choose from the set. | |
||||
|
|
||||
|
<a name="module_G.choices"></a> |
||||
|
|
||||
|
### G.choices(n, k, [options]) ⇒ <code>Number</code> |
||||
|
Higher level method for counting number of possible combinations of "k" elements from a set of size "n". |
||||
|
|
||||
|
**Kind**: static method of <code>[G](#module_G)</code> |
||||
|
**Returns**: <code>Number</code> - Number of possible combinations. |
||||
|
|
||||
|
| Param | Type | Description | |
||||
|
| --- | --- | --- | |
||||
|
| n | <code>Number</code> | Number of elements in the set. | |
||||
|
| k | <code>Number</code> | Number of elements to choose from the set. | |
||||
|
| [options] | <code>Object</code> | | |
||||
|
| options.replace | <code>Boolean</code> | Is replacement allowed after each choice? | |
||||
|
| options.ordered | <code>Boolean</code> | Does the order of the choices matter? | |
||||
|
|
||||
|
<a name="module_G.combination"></a> |
||||
|
|
||||
|
### G.combination(arr, [size]) ⇒ <code>Generator</code> |
||||
|
Generates all combinations of a set. |
||||
|
|
||||
|
**Kind**: static method of <code>[G](#module_G)</code> |
||||
|
**Returns**: <code>Generator</code> - yields each combination as an array |
||||
|
|
||||
|
| Param | Type | Default | Description | |
||||
|
| --- | --- | --- | --- | |
||||
|
| arr | <code>Array</code> | <code>String</code> | | The set of elements. | |
||||
|
| [size] | <code>Number</code> | <code>arr.length</code> | Number of elements to choose from the set. | |
||||
|
|
||||
|
<a name="module_G.permutation"></a> |
||||
|
|
||||
|
### G.permutation(arr, [size]) ⇒ <code>Generator</code> |
||||
|
Generates all permutations of a set. |
||||
|
|
||||
|
**Kind**: static method of <code>[G](#module_G)</code> |
||||
|
**Returns**: <code>Generator</code> - yields each permutation as an array |
||||
|
|
||||
|
| Param | Type | Default | Description | |
||||
|
| --- | --- | --- | --- | |
||||
|
| arr | <code>Array</code> | <code>String</code> | | The set of elements. | |
||||
|
| [size] | <code>Number</code> | <code>arr.length</code> | Number of elements to choose from the set. | |
||||
|
|
||||
|
<a name="module_G.powerSet"></a> |
||||
|
|
||||
|
### G.powerSet(arr) ⇒ <code>Generator</code> |
||||
|
Generates all possible subsets of a set (a.k.a. power set). |
||||
|
|
||||
|
**Kind**: static method of <code>[G](#module_G)</code> |
||||
|
**Returns**: <code>Generator</code> - yields each subset as an array |
||||
|
|
||||
|
| Param | Type | Description | |
||||
|
| --- | --- | --- | |
||||
|
| arr | <code>Array</code> | <code>String</code> | The set of elements. | |
||||
|
|
||||
|
<a name="module_G.permutationCombination"></a> |
||||
|
|
||||
|
### G.permutationCombination(arr) ⇒ <code>Generator</code> |
||||
|
Generates the permutation of the combinations of a set. |
||||
|
|
||||
|
**Kind**: static method of <code>[G](#module_G)</code> |
||||
|
**Returns**: <code>Generator</code> - yields each permutation as an array |
||||
|
|
||||
|
| Param | Type | Description | |
||||
|
| --- | --- | --- | |
||||
|
| arr | <code>Array</code> | <code>String</code> | The set of elements. | |
||||
|
|
||||
|
<a name="module_G.baseN"></a> |
||||
|
|
||||
|
### G.baseN(arr, [size]) ⇒ <code>Generator</code> |
||||
|
Generates all possible "numbers" from the digits of a set. |
||||
|
|
||||
|
**Kind**: static method of <code>[G](#module_G)</code> |
||||
|
**Returns**: <code>Generator</code> - yields all digits as an array |
||||
|
|
||||
|
| Param | Type | Default | Description | |
||||
|
| --- | --- | --- | --- | |
||||
|
| arr | <code>Array</code> | <code>String</code> | | The set of digits. | |
||||
|
| [size] | <code>Number</code> | <code>arr.length</code> | How many digits will be in the numbers. | |
||||
|
|
||||
|
<a name="module_G.baseNAll"></a> |
||||
|
|
||||
|
### G.baseNAll(arr) ⇒ <code>Generator</code> |
||||
|
Infinite generator for all possible "numbers" from a set of digits. |
||||
|
|
||||
|
**Kind**: static method of <code>[G](#module_G)</code> |
||||
|
**Returns**: <code>Generator</code> - yields all digits as an array |
||||
|
|
||||
|
| Param | Type | Description | |
||||
|
| --- | --- | --- | |
||||
|
| arr | <code>Array</code> | <code>String</code> | The set of digits | |
||||
|
|
||||
|
<a name="module_G.cartesian"></a> |
||||
|
|
||||
|
### G.cartesian(...sets) ⇒ <code>Generator</code> |
||||
|
Generates the cartesian product of the sets. |
||||
|
|
||||
|
**Kind**: static method of <code>[G](#module_G)</code> |
||||
|
**Returns**: <code>Generator</code> - yields each product as an array |
||||
|
|
||||
|
| Param | Type | Description | |
||||
|
| --- | --- | --- | |
||||
|
| ...sets | <code>Array</code> | <code>String</code> | variable number of sets of n elements. | |
||||
|
|
||||
|
<a name="module_G.shuffle"></a> |
||||
|
|
||||
|
### G.shuffle(arr) ⇒ <code>Array</code> |
||||
|
Shuffles an array in place using the Fisher–Yates shuffle. |
||||
|
|
||||
|
**Kind**: static method of <code>[G](#module_G)</code> |
||||
|
**Returns**: <code>Array</code> - a random, unbiased perutation of arr |
||||
|
|
||||
|
| Param | Type | Description | |
||||
|
| --- | --- | --- | |
||||
|
| arr | <code>Array</code> | A set of elements. | |
||||
|
|
@ -0,0 +1,32 @@ |
|||||
|
{ |
||||
|
"name": "generatorics", |
||||
|
"version": "1.0.5", |
||||
|
"description": "Combinatorics library for JavaScript using ES2015 generator functions. Generate power set, combination, and permutation.", |
||||
|
"main": "generatorics.js", |
||||
|
"authors": [ |
||||
|
"Andrew Carlson <acarl005@g.ucla.edu>" |
||||
|
], |
||||
|
"license": "MIT", |
||||
|
"keywords": [ |
||||
|
"combinatorics", |
||||
|
"combination", |
||||
|
"permutation", |
||||
|
"powerset", |
||||
|
"factoradic", |
||||
|
"shuffle" |
||||
|
], |
||||
|
"homepage": "https://github.com/acarl005/generatorics", |
||||
|
"moduleType": [ |
||||
|
"amd", |
||||
|
"es6", |
||||
|
"globals", |
||||
|
"node" |
||||
|
], |
||||
|
"ignore": [ |
||||
|
"**/.*", |
||||
|
"node_modules", |
||||
|
"bower_components", |
||||
|
"test", |
||||
|
"tests" |
||||
|
] |
||||
|
} |
@ -0,0 +1,328 @@ |
|||||
|
/* |
||||
|
* Licensed under the MIT license. |
||||
|
* http://www.opensource.org/licenses/mit-license.php
|
||||
|
* |
||||
|
* References: |
||||
|
* http://www.ruby-doc.org/core-2.0/Array.html#method-i-combination
|
||||
|
* http://www.ruby-doc.org/core-2.0/Array.html#method-i-permutation
|
||||
|
*/ |
||||
|
|
||||
|
(function(root, factory) { |
||||
|
if (typeof define === 'function' && define.amd) { |
||||
|
define([], factory) |
||||
|
} else if (typeof exports === 'object') { |
||||
|
module.exports = factory() |
||||
|
} else { |
||||
|
root.G = factory() |
||||
|
} |
||||
|
}(this, function() { |
||||
|
|
||||
|
'use strict' |
||||
|
|
||||
|
/** @exports G */ |
||||
|
const G = { |
||||
|
|
||||
|
clones: false, |
||||
|
|
||||
|
/** |
||||
|
* Calculates a factorial |
||||
|
* @param {Number} n - The number to operate the factorial on. |
||||
|
* @returns {Number} n! |
||||
|
*/ |
||||
|
factorial: function factorial(n) { |
||||
|
for (var ans = 1; n; ans *= n--); |
||||
|
return ans |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Converts a number to the factorial number system. Digits are in least significant order. |
||||
|
* @param {Number} n - Integer in base 10 |
||||
|
* @returns {Array} digits of n in factoradic in least significant order |
||||
|
*/ |
||||
|
factoradic: function factoradic(n) { |
||||
|
let radix = 1 |
||||
|
for (var digit = 1; radix < n; radix *= ++digit); |
||||
|
if (radix > n) radix /= digit-- |
||||
|
let result = [0] |
||||
|
for (; digit; radix /= digit--) { |
||||
|
result[digit] = Math.floor(n / radix) |
||||
|
n %= radix |
||||
|
} |
||||
|
return result |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Calculates the number of possible permutations of "k" elements in a set of size "n". |
||||
|
* @param {Number} n - Number of elements in the set. |
||||
|
* @param {Number} k - Number of elements to choose from the set. |
||||
|
* @returns {Number} n P k |
||||
|
*/ |
||||
|
P: function P(n, k) { |
||||
|
return this.factorial(n) / this.factorial(n - k) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Calculates the number of possible combinations of "k" elements in a set of size "n". |
||||
|
* @param {Number} n - Number of elements in the set. |
||||
|
* @param {Number} k - Number of elements to choose from the set. |
||||
|
* @returns {Number} n C k |
||||
|
*/ |
||||
|
C: function C(n, k) { |
||||
|
return this.P(n, k) / this.factorial(k) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Higher level method for counting number of possible combinations of "k" elements from a set of size "n". |
||||
|
* @param {Number} n - Number of elements in the set. |
||||
|
* @param {Number} k - Number of elements to choose from the set. |
||||
|
* @param {Object} [options] |
||||
|
* @param {Boolean} options.replace - Is replacement allowed after each choice? |
||||
|
* @param {Boolean} options.ordered - Does the order of the choices matter? |
||||
|
* @returns {Number} Number of possible combinations. |
||||
|
*/ |
||||
|
choices: function choices(n, k, options = {}) { |
||||
|
if (options.replace) { |
||||
|
if (options.ordered) { |
||||
|
return Math.pow(n, k) |
||||
|
} else { |
||||
|
return this.C(n + k - 1, k) |
||||
|
} |
||||
|
} else { |
||||
|
if (options.ordered) { |
||||
|
return this.P(n, k) |
||||
|
} else { |
||||
|
return this.C(n, k) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Generates all combinations of a set. |
||||
|
* @param {Array|String} arr - The set of elements. |
||||
|
* @param {Number} [size=arr.length] - Number of elements to choose from the set. |
||||
|
* @returns {Generator} yields each combination as an array |
||||
|
*/ |
||||
|
combination: function* combination(arr, size = arr.length) { |
||||
|
let that = this |
||||
|
let end = arr.length - 1 |
||||
|
let data = [] |
||||
|
yield* combinationUtil(0, 0) |
||||
|
function* combinationUtil(start, index) { |
||||
|
if (index === size) { // Current combination is ready to be processed, yield it
|
||||
|
return yield that.clones ? data.slice() : data // .slice() is a JS idiom for shallow cloning an array
|
||||
|
} |
||||
|
// replace index with all possible elements. The condition
|
||||
|
// "end - i + 1 >= size - index" makes sure that including one element
|
||||
|
// at index will make a combination with remaining elements
|
||||
|
// at remaining positions
|
||||
|
for (let i = start; i <= end && end - i + 1 >= size - index; i++) { |
||||
|
data[index] = arr[i] |
||||
|
yield* combinationUtil(i + 1, index + 1) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Generates all permutations of a set. |
||||
|
* @param {Array|String} arr - The set of elements. |
||||
|
* @param {Number} [size=arr.length] - Number of elements to choose from the set. |
||||
|
* @returns {Generator} yields each permutation as an array |
||||
|
*/ |
||||
|
permutation: function* permutation(arr, size = arr.length) { |
||||
|
let that = this |
||||
|
let len = arr.length |
||||
|
if (size === len) { // switch to Heap's algorithm. it's more efficient
|
||||
|
return yield* heapsAlg(arr, that.clones) |
||||
|
} |
||||
|
let data = [] |
||||
|
let indecesUsed = [] // permutations do not repeat elements. keep track of the indeces of the elements already used
|
||||
|
yield* permutationUtil(0) |
||||
|
function* permutationUtil(index) { |
||||
|
if (index === size) { |
||||
|
return yield that.clones ? data.slice() : data |
||||
|
} |
||||
|
for (let i = 0; i < len; i++) { |
||||
|
if (!indecesUsed[i]) { |
||||
|
indecesUsed[i] = true |
||||
|
data[index] = arr[i] |
||||
|
yield *permutationUtil(index + 1) |
||||
|
indecesUsed[i] = false |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Generates all possible subsets of a set (a.k.a. power set). |
||||
|
* @param {Array|String} arr - The set of elements. |
||||
|
* @returns {Generator} yields each subset as an array |
||||
|
*/ |
||||
|
powerSet: function* powerSet(arr) { |
||||
|
let that = this |
||||
|
let len = arr.length |
||||
|
let data = [] |
||||
|
yield* powerUtil(0, 0) |
||||
|
function* powerUtil(start, index) { |
||||
|
data.length = index |
||||
|
yield that.clones ? data.slice() : data |
||||
|
if (index === len) { |
||||
|
return |
||||
|
} |
||||
|
for (let i = start; i < len; i++) { |
||||
|
data[index] = arr[i] |
||||
|
yield* powerUtil(i + 1, index + 1) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Generates the permutation of the combinations of a set. |
||||
|
* @param {Array|String} arr - The set of elements. |
||||
|
* @returns {Generator} yields each permutation as an array |
||||
|
*/ |
||||
|
permutationCombination: function* permutationCombination(arr) { |
||||
|
let that = this |
||||
|
let len = arr.length |
||||
|
let data = [] |
||||
|
let indecesUsed = [] |
||||
|
yield* permutationUtil(0) |
||||
|
function* permutationUtil(index) { |
||||
|
data.length = index |
||||
|
yield that.clones ? data.slice() : data |
||||
|
if (index === len) { |
||||
|
return |
||||
|
} |
||||
|
for (let i = 0; i < len; i++) { |
||||
|
if (!indecesUsed[i]) { |
||||
|
indecesUsed[i] = true |
||||
|
data[index] = arr[i] |
||||
|
yield *permutationUtil(index + 1) |
||||
|
indecesUsed[i] = false |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Generates all possible "numbers" from the digits of a set. |
||||
|
* @param {Array|String} arr - The set of digits. |
||||
|
* @param {Number} [size=arr.length] - How many digits will be in the numbers. |
||||
|
* @returns {Generator} yields all digits as an array |
||||
|
*/ |
||||
|
baseN: function* baseN(arr, size = arr.length) { |
||||
|
let that = this |
||||
|
let len = arr.length |
||||
|
let data = [] |
||||
|
yield* baseNUtil(0) |
||||
|
function* baseNUtil(index) { |
||||
|
if (index === size) { |
||||
|
return yield that.clones ? data.slice() : data |
||||
|
} |
||||
|
for (let i = 0; i < len; i++) { |
||||
|
data[index] = arr[i] |
||||
|
yield* baseNUtil(index + 1) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Infinite generator for all possible "numbers" from a set of digits. |
||||
|
* @param {Array|String} arr - The set of digits |
||||
|
* @returns {Generator} yields all digits as an array |
||||
|
*/ |
||||
|
baseNAll: function* permutationAll(arr) { |
||||
|
for (let len = 1; true; len++) { |
||||
|
yield* this.baseN(arr, len) |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Generates the cartesian product of the sets. |
||||
|
* @param {...(Array|String)} sets - variable number of sets of n elements. |
||||
|
* @returns {Generator} yields each product as an array |
||||
|
*/ |
||||
|
cartesian: function* cartesian(...sets) { |
||||
|
let that = this |
||||
|
let data = [] |
||||
|
yield* cartesianUtil(0) |
||||
|
function* cartesianUtil(index) { |
||||
|
if (index === sets.length) { |
||||
|
return yield that.clones ? data.slice() : data |
||||
|
} |
||||
|
for (let i = 0; i < sets[index].length; i++) { |
||||
|
data[index] = sets[index][i] |
||||
|
yield* cartesianUtil(index + 1) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Shuffles an array in place using the Fisher–Yates shuffle. |
||||
|
* @param {Array} arr - A set of elements. |
||||
|
* @returns {Array} a random, unbiased perutation of arr |
||||
|
*/ |
||||
|
shuffle: function shuffle(arr) { |
||||
|
for (let i = arr.length - 1; i > 0; i--) { |
||||
|
let j = Math.floor(Math.random() * (i + 1)) |
||||
|
swap(arr, i, j) |
||||
|
} |
||||
|
return arr |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
let clone = { clones: true } |
||||
|
clone.combination = G.combination |
||||
|
clone.permutation = G.permutation |
||||
|
clone.powerSet = G.powerSet |
||||
|
clone.permutationCombination = G.permutationCombination |
||||
|
clone.baseN = G.baseN |
||||
|
clone.baseNAll = G.baseNAll |
||||
|
clone.cartesian = G.cartesian |
||||
|
|
||||
|
G.clone = clone |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* |
||||
|
* More efficient alorithm for permutations of All elements in an array. Doesn't |
||||
|
* work for "sub-permutations", e.g. permutations of 3 elements from [1, 2, 3, 4, 5] |
||||
|
*/ |
||||
|
function* heapsAlg(arr, clone) { |
||||
|
let size = arr.length |
||||
|
if (typeof arr === 'string') { |
||||
|
arr = arr.split('') |
||||
|
} |
||||
|
yield* heapsUtil(0) |
||||
|
function* heapsUtil(index) { |
||||
|
if (index === size) { |
||||
|
return yield clone ? arr.slice() : arr |
||||
|
} |
||||
|
|
||||
|
for (let j = index; j < size; j++) { |
||||
|
swap(arr, index, j) |
||||
|
yield* heapsUtil(index + 1) |
||||
|
swap(arr, index, j) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* Swaps two array elements. |
||||
|
*/ |
||||
|
function swap(arr, i, j) { |
||||
|
let len = arr.length |
||||
|
if (i >= len || j >= len) { |
||||
|
console.warn('Swapping an array\'s elements past its length.') |
||||
|
} |
||||
|
let temp = arr[j] |
||||
|
arr[j] = arr[i] |
||||
|
arr[i] = temp |
||||
|
return arr |
||||
|
} |
||||
|
|
||||
|
|
||||
|
return G |
||||
|
|
||||
|
})) |
@ -0,0 +1,39 @@ |
|||||
|
{ |
||||
|
"name": "generatorics", |
||||
|
"version": "1.1.0", |
||||
|
"description": "Efficient Combinatorics library for JavaScript using ES2015 generator functions. Generate power set, combination, and permutation.", |
||||
|
"main": "generatorics.js", |
||||
|
"engines": { |
||||
|
"node": ">=6.0.0" |
||||
|
}, |
||||
|
"directories": { |
||||
|
"test": "test" |
||||
|
}, |
||||
|
"dependencies": {}, |
||||
|
"devDependencies": { |
||||
|
"chai": "^3.5.0", |
||||
|
"eslint": "^3.14.0", |
||||
|
"jsdoc-to-markdown": "^3.0.0", |
||||
|
"mocha": "^2.4.5" |
||||
|
}, |
||||
|
"scripts": { |
||||
|
"lint": "eslint generatorics.js", |
||||
|
"test": "mocha", |
||||
|
"docs": "sed -n '/Documentation/q;p' README.md > README.bak.md; echo '## Documentation' >> README.bak.md; jsdoc2md generatorics.js >> README.bak.md && mv -f README.bak.md README.md" |
||||
|
}, |
||||
|
"author": "Andrew Carlson <acarl005@g.ucla.edu>", |
||||
|
"repository": { |
||||
|
"type": "git", |
||||
|
"url": "git+https://github.com/acarl005/generatorics.git" |
||||
|
}, |
||||
|
"homepage": "https://github.com/acarl005/generatorics#readme", |
||||
|
"license": "MIT", |
||||
|
"keywords": [ |
||||
|
"combinatorics", |
||||
|
"combination", |
||||
|
"permutation", |
||||
|
"powerset", |
||||
|
"factoradic", |
||||
|
"shuffle" |
||||
|
] |
||||
|
} |
@ -0,0 +1,529 @@ |
|||||
|
var expect = require('chai').expect |
||||
|
var G = require('../generatorics') |
||||
|
|
||||
|
describe('Arithmetic functions', () => { |
||||
|
it('factorial works', () => { |
||||
|
expect(G.factorial(0)).to.equal(1) |
||||
|
expect(G.factorial(1)).to.equal(1) |
||||
|
expect(G.factorial(2)).to.equal(2) |
||||
|
expect(G.factorial(3)).to.equal(6) |
||||
|
expect(G.factorial(4)).to.equal(24) |
||||
|
expect(G.factorial(5)).to.equal(120) |
||||
|
}) |
||||
|
|
||||
|
it('factoradic works', () => { |
||||
|
expect(G.factoradic(0)).to.eql([ 0 ]) |
||||
|
expect(G.factoradic(1)).to.eql([ 0, 1 ]) |
||||
|
expect(G.factoradic(2)).to.eql([ 0, 0, 1 ]) |
||||
|
expect(G.factoradic(3)).to.eql([ 0, 1, 1 ]) |
||||
|
expect(G.factoradic(100)).to.eql([ 0, 0, 2, 0, 4 ]) |
||||
|
expect(G.factoradic(1337)).to.eql([ 0, 1, 2, 2, 0, 5, 1 ]) |
||||
|
expect(G.factoradic(9001)).to.eql([ 0, 1, 0, 0, 0, 3, 5, 1 ]) |
||||
|
expect(G.factoradic(3958174309503149571029856012)).to.eql([ 0, 1, 0, 2, 2, 3, 5, 5, 2, 2, 3, 7, 6, 1, 12, 14, 11, 12, 18, 0, 10, 2, 21, 12, 4, 21, 9 ]) |
||||
|
}) |
||||
|
|
||||
|
it('P(n,r) works', () => { |
||||
|
expect(G.P(10, 3)).to.equal(720) |
||||
|
expect(G.P(10, 1)).to.equal(10) |
||||
|
expect(G.P(10, 0)).to.equal(1) |
||||
|
expect(G.P(10, 10)).to.equal(3628800) |
||||
|
}) |
||||
|
|
||||
|
it('C(n,r) works', () => { |
||||
|
expect(G.C(10, 3)).to.equal(120) |
||||
|
expect(G.C(10, 1)).to.equal(10) |
||||
|
expect(G.C(10, 0)).to.equal(1) |
||||
|
expect(G.C(10, 10)).to.equal(1) |
||||
|
}) |
||||
|
|
||||
|
it('higher level method for counting choices', () => { |
||||
|
expect(G.choices(10, 3, { replace: true, ordered: true })).to.equal(1000) |
||||
|
expect(G.choices(10, 3, { replace: true, ordered: false })).to.equal(220) |
||||
|
expect(G.choices(10, 3, { replace: false, ordered: true })).to.equal(720) |
||||
|
expect(G.choices(10, 3, { replace: false, ordered: false })).to.equal(120) |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
describe('Combinations', () => { |
||||
|
it('should get combinations of two from set of 3', () => { |
||||
|
var members = [ |
||||
|
[ 1, 2 ], |
||||
|
[ 1, 3 ], |
||||
|
[ 2, 3 ] |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var comb of G.combination([1, 2, 3], 2)) { |
||||
|
answers.push(comb.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('should work with size 1', () => { |
||||
|
var members = [ |
||||
|
[ 1 ], |
||||
|
[ 2 ], |
||||
|
[ 3 ] |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var comb of G.combination([1, 2, 3], 1)) { |
||||
|
answers.push(comb.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('should work with size 0', () => { |
||||
|
for (var comb of G.combination([1, 2, 3], 0)) { |
||||
|
expect(comb).to.eql([]) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
it('should yield nothing if size is greater than array length', () => { |
||||
|
for (var comb of G.combination([1, 2, 3], 4)) { |
||||
|
throw new Error('Made a combination when it should not have') |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
it('combinations should default to arr.length without size specified', () => { |
||||
|
for (var comb of G.combination([1, 2, 3])) { |
||||
|
expect(comb).to.eql([1, 2, 3]) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
it('should work with strings', () => { |
||||
|
var members = [ |
||||
|
[ 'a', 'b' ], |
||||
|
[ 'a', 'c' ], |
||||
|
[ 'a', 'd' ], |
||||
|
[ 'b', 'c' ], |
||||
|
[ 'b', 'd' ], |
||||
|
[ 'c', 'd' ], |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var comb of G.combination('abcd', 2)) { |
||||
|
answers.push(comb.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('yields the same array each time', () => { |
||||
|
var arr = [...G.combination([1, 2, 3], 2)] |
||||
|
expect(arr).to.eql(Array(arr.length).fill(arr[0])) |
||||
|
}) |
||||
|
|
||||
|
}) |
||||
|
|
||||
|
describe('Permutations', () => { |
||||
|
|
||||
|
it('should get permutations of 2 from a set of 3', () => { |
||||
|
var members = [ |
||||
|
[ 1, 2 ], |
||||
|
[ 1, 3 ], |
||||
|
[ 2, 1 ], |
||||
|
[ 2, 3 ], |
||||
|
[ 3, 1 ], |
||||
|
[ 3, 2 ] |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var perm of G.permutation([1, 2, 3], 2)) { |
||||
|
answers.push(perm.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('should work with size 1', () => { |
||||
|
var members = [ |
||||
|
[ 1 ], |
||||
|
[ 2 ], |
||||
|
[ 3 ] |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var perm of G.permutation([1, 2, 3], 1)) { |
||||
|
answers.push(perm.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('should work with size 0', () => { |
||||
|
for (var perm of G.permutation([1, 2, 3], 0)) { |
||||
|
expect(perm).to.eql([]) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
it('should yield nothing if size is greater than array length', () => { |
||||
|
for (var perm of G.permutation([1, 2, 3], 4)) { |
||||
|
throw new Error('Made a permutation when it should not have') |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
it('permutations should default to arr.length without size specified', () => { |
||||
|
var members = [ |
||||
|
[ 1, 2, 3 ], |
||||
|
[ 1, 3, 2 ], |
||||
|
[ 2, 1, 3 ], |
||||
|
[ 2, 3, 1 ], |
||||
|
[ 3, 2, 1 ], |
||||
|
[ 3, 1, 2 ], |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var perm of G.permutation([1, 2, 3])) { |
||||
|
answers.push(perm.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('should work with strings', () => { |
||||
|
var members = [ |
||||
|
[ 'a', 'b' ], |
||||
|
[ 'a', 'c' ], |
||||
|
[ 'a', 'd' ], |
||||
|
[ 'b', 'a' ], |
||||
|
[ 'b', 'c' ], |
||||
|
[ 'b', 'd' ], |
||||
|
[ 'c', 'a' ], |
||||
|
[ 'c', 'b' ], |
||||
|
[ 'c', 'd' ], |
||||
|
[ 'd', 'a' ], |
||||
|
[ 'd', 'b' ], |
||||
|
[ 'd', 'c' ], |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var perm of G.permutation('abcd', 2)) { |
||||
|
answers.push(perm.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('should work with string without specifying length', () => { |
||||
|
var members = [ |
||||
|
[ 'a', 'b', 'c' ], |
||||
|
[ 'a', 'c', 'b' ], |
||||
|
[ 'b', 'a', 'c' ], |
||||
|
[ 'b', 'c', 'a' ], |
||||
|
[ 'c', 'b', 'a' ], |
||||
|
[ 'c', 'a', 'b' ] |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var perm of G.permutation('abc')) { |
||||
|
answers.push(perm.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('yields the same array each time', () => { |
||||
|
var arr = [...G.permutation([1, 2, 3], 2)] |
||||
|
expect(arr).to.eql(Array(arr.length).fill(arr[0])) |
||||
|
}) |
||||
|
|
||||
|
}) |
||||
|
|
||||
|
describe('Base N', () => { |
||||
|
|
||||
|
it('should get number of 2 digits a set of 3', () => { |
||||
|
var members = [ |
||||
|
[ 1, 1 ], |
||||
|
[ 1, 2 ], |
||||
|
[ 1, 3 ], |
||||
|
[ 2, 1 ], |
||||
|
[ 2, 2 ], |
||||
|
[ 2, 3 ], |
||||
|
[ 3, 1 ], |
||||
|
[ 3, 2 ], |
||||
|
[ 3, 3 ], |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var perm of G.baseN([1, 2, 3], 2)) { |
||||
|
answers.push(perm.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('should work with size 1', () => { |
||||
|
var members = [ |
||||
|
[ 1 ], |
||||
|
[ 2 ], |
||||
|
[ 3 ] |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var perm of G.baseN([1, 2, 3], 1)) { |
||||
|
answers.push(perm.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('should work with size 0', () => { |
||||
|
for (var perm of G.baseN([1, 2, 3], 0)) { |
||||
|
expect(perm).to.eql([]) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
it('baseN should default to arr.length without size specified', () => { |
||||
|
var members = [ |
||||
|
[ 1, 1, 1 ], |
||||
|
[ 1, 1, 2 ], |
||||
|
[ 1, 1, 3 ], |
||||
|
[ 1, 2, 1 ], |
||||
|
[ 1, 2, 2 ], |
||||
|
[ 1, 2, 3 ], |
||||
|
[ 1, 3, 1 ], |
||||
|
[ 1, 3, 2 ], |
||||
|
[ 1, 3, 3 ], |
||||
|
[ 2, 1, 1 ], |
||||
|
[ 2, 1, 2 ], |
||||
|
[ 2, 1, 3 ], |
||||
|
[ 2, 2, 1 ], |
||||
|
[ 2, 2, 2 ], |
||||
|
[ 2, 2, 3 ], |
||||
|
[ 2, 3, 1 ], |
||||
|
[ 2, 3, 2 ], |
||||
|
[ 2, 3, 3 ], |
||||
|
[ 3, 1, 1 ], |
||||
|
[ 3, 1, 2 ], |
||||
|
[ 3, 1, 3 ], |
||||
|
[ 3, 2, 1 ], |
||||
|
[ 3, 2, 2 ], |
||||
|
[ 3, 2, 3 ], |
||||
|
[ 3, 3, 1 ], |
||||
|
[ 3, 3, 2 ], |
||||
|
[ 3, 3, 3 ], |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var perm of G.baseN([1, 2, 3])) { |
||||
|
answers.push(perm.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('yields the same array each time', () => { |
||||
|
var arr = [...G.baseN([1, 2, 3])] |
||||
|
expect(arr).to.eql(Array(arr.length).fill(arr[0])) |
||||
|
}) |
||||
|
|
||||
|
}) |
||||
|
|
||||
|
|
||||
|
describe('Power Set', () => { |
||||
|
|
||||
|
it('should calculate power set', () => { |
||||
|
var members = [ |
||||
|
[ ], |
||||
|
[ 1 ], |
||||
|
[ 2 ], |
||||
|
[ 1, 2 ], |
||||
|
[ 3 ], |
||||
|
[ 1, 3 ], |
||||
|
[ 2, 3 ], |
||||
|
[ 1, 2, 3 ] |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var sett of G.powerSet([1, 2, 3])) { |
||||
|
answers.push(sett.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('yields the same array each time', () => { |
||||
|
var arr = [...G.powerSet([1, 2, 3])] |
||||
|
expect(arr).to.eql(Array(arr.length).fill(arr[0])) |
||||
|
}) |
||||
|
|
||||
|
}) |
||||
|
|
||||
|
describe('Permutation Combination', () => { |
||||
|
|
||||
|
it('should get the permutation of combinations', () => { |
||||
|
var members = [ |
||||
|
[ ], |
||||
|
[ 'a' ], |
||||
|
[ 'b' ], |
||||
|
[ 'c' ], |
||||
|
[ 'a', 'b' ], |
||||
|
[ 'b', 'a' ], |
||||
|
[ 'a', 'c' ], |
||||
|
[ 'c', 'a' ], |
||||
|
[ 'b', 'c' ], |
||||
|
[ 'c', 'b' ], |
||||
|
[ 'a', 'b', 'c' ], |
||||
|
[ 'a', 'c', 'b' ], |
||||
|
[ 'b', 'a', 'c' ], |
||||
|
[ 'b', 'c', 'a' ], |
||||
|
[ 'c', 'a', 'b' ], |
||||
|
[ 'c', 'b', 'a' ] |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var comb of G.permutationCombination(['a', 'b', 'c'])) { |
||||
|
answers.push(comb.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('should yield the same array each time', () => { |
||||
|
var arr = [...G.permutationCombination([1, 2, 3])] |
||||
|
expect(arr).to.eql(Array(arr.length).fill(arr[0])) |
||||
|
}) |
||||
|
|
||||
|
}) |
||||
|
|
||||
|
describe('Cartesian Product', () => { |
||||
|
|
||||
|
it('should find the cartesian product of a bunch of arrays', () => { |
||||
|
var members = [ |
||||
|
[0, 0, 0], [1, 0, 0], [2, 0, 0], |
||||
|
[0, 10, 0], [1, 10, 0], [2, 10, 0], |
||||
|
[0, 20, 0], [1, 20, 0], [2, 20, 0], |
||||
|
[0, 0, 100], [1, 0, 100], [2, 0, 100], |
||||
|
[0, 10, 100],[1, 10, 100],[2, 10, 100], |
||||
|
[0, 20, 100],[1, 20, 100],[2, 20, 100], |
||||
|
[0, 0, 200], [1, 0, 200], [2, 0, 200], |
||||
|
[0, 10, 200],[1, 10, 200],[2, 10, 200], |
||||
|
[0, 20, 200],[1, 20, 200],[2, 20, 200] |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var sett of G.cartesian([0, 1, 2], [0, 10, 20], [0, 100, 200])) { |
||||
|
answers.push(sett.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('should work with apply', () => { |
||||
|
var members = [ |
||||
|
[0, 0, 0], [1, 0, 0], [2, 0, 0], |
||||
|
[0, 10, 0], [1, 10, 0], [2, 10, 0], |
||||
|
[0, 20, 0], [1, 20, 0], [2, 20, 0], |
||||
|
[0, 0, 100], [1, 0, 100], [2, 0, 100], |
||||
|
[0, 10, 100],[1, 10, 100],[2, 10, 100], |
||||
|
[0, 20, 100],[1, 20, 100],[2, 20, 100], |
||||
|
[0, 0, 200], [1, 0, 200], [2, 0, 200], |
||||
|
[0, 10, 200],[1, 10, 200],[2, 10, 200], |
||||
|
[0, 20, 200],[1, 20, 200],[2, 20, 200] |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var sett of G.cartesian.apply(G, [[0, 1, 2], [0, 10, 20], [0, 100, 200]])) { |
||||
|
answers.push(sett.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('should work with strings', () => { |
||||
|
var members = [ |
||||
|
[ 'a', 'c', 'e' ], |
||||
|
[ 'a', 'c', 'f' ], |
||||
|
[ 'a', 'd', 'e' ], |
||||
|
[ 'a', 'd', 'f' ], |
||||
|
[ 'b', 'c', 'e' ], |
||||
|
[ 'b', 'c', 'f' ], |
||||
|
[ 'b', 'd', 'e' ], |
||||
|
[ 'b', 'd', 'f' ] |
||||
|
] |
||||
|
var answers = [] |
||||
|
for (var sett of G.cartesian('ab', 'cd', 'ef')) { |
||||
|
answers.push(sett.slice()) |
||||
|
} |
||||
|
expect(answers).to.have.length(members.length) |
||||
|
expect(answers).to.deep.have.members(members) |
||||
|
}) |
||||
|
|
||||
|
it('should yield same array each time', () => { |
||||
|
var arr = [...G.cartesian('12', '34', '56')] |
||||
|
expect(arr).to.eql(Array(arr.length).fill(arr[0])) |
||||
|
}) |
||||
|
|
||||
|
}) |
||||
|
|
||||
|
|
||||
|
describe('Infinite Base N', () => { |
||||
|
|
||||
|
it('generates all permutations of an array', () => { |
||||
|
var members = [ |
||||
|
[ 'a' ], [ 'b' ], [ 'c' ], [ 'a', 'a' ], [ 'a', 'b' ], [ 'a', 'c' ], [ 'b', 'a' ], [ 'b', 'b' ], [ 'b', 'c' ], [ 'c', 'a' ], [ 'c', 'b' ], [ 'c', 'c' ], |
||||
|
[ 'a', 'a', 'a' ], [ 'a', 'a', 'b' ], [ 'a', 'a', 'c' ], [ 'a', 'b', 'a' ], [ 'a', 'b', 'b' ], [ 'a', 'b', 'c' ], [ 'a', 'c', 'a' ], [ 'a', 'c', 'b' ], |
||||
|
[ 'a', 'c', 'c' ], [ 'b', 'a', 'a' ], [ 'b', 'a', 'b' ], [ 'b', 'a', 'c' ], [ 'b', 'b', 'a' ], [ 'b', 'b', 'b' ], [ 'b', 'b', 'c' ], [ 'b', 'c', 'a' ], |
||||
|
[ 'b', 'c', 'b' ], [ 'b', 'c', 'c' ], [ 'c', 'a', 'a' ], [ 'c', 'a', 'b' ], [ 'c', 'a', 'c' ], [ 'c', 'b', 'a' ], [ 'c', 'b', 'b' ], [ 'c', 'b', 'c' ], |
||||
|
[ 'c', 'c', 'a' ], [ 'c', 'c', 'b' ], [ 'c', 'c', 'c' ], [ 'a', 'a', 'a', 'a' ], [ 'a', 'a', 'a', 'b' ], [ 'a', 'a', 'a', 'c' ], [ 'a', 'a', 'b', 'a' ], |
||||
|
[ 'a', 'a', 'b', 'b' ], [ 'a', 'a', 'b', 'c' ], [ 'a', 'a', 'c', 'a' ], [ 'a', 'a', 'c', 'b' ], [ 'a', 'a', 'c', 'c' ], [ 'a', 'b', 'a', 'a' ], [ 'a', 'b', 'a', 'b' ], |
||||
|
[ 'a', 'b', 'a', 'c' ], [ 'a', 'b', 'b', 'a' ], [ 'a', 'b', 'b', 'b' ], [ 'a', 'b', 'b', 'c' ], [ 'a', 'b', 'c', 'a' ], [ 'a', 'b', 'c', 'b' ], [ 'a', 'b', 'c', 'c' ], |
||||
|
[ 'a', 'c', 'a', 'a' ], [ 'a', 'c', 'a', 'b' ], [ 'a', 'c', 'a', 'c' ], [ 'a', 'c', 'b', 'a' ], [ 'a', 'c', 'b', 'b' ], [ 'a', 'c', 'b', 'c' ], [ 'a', 'c', 'c', 'a' ], |
||||
|
] |
||||
|
var nums = [ 'a', 'b', 'c' ] |
||||
|
var perms = [] |
||||
|
var gen = G.baseNAll(nums) |
||||
|
for (var i = 0; i < 64; i++) { |
||||
|
perms.push(gen.next().value.slice()) |
||||
|
} |
||||
|
expect(perms).to.eql(members) |
||||
|
}) |
||||
|
|
||||
|
}) |
||||
|
|
||||
|
|
||||
|
describe('Clone submodule', () => { |
||||
|
|
||||
|
it('clones combinations', () => { |
||||
|
var answers = [] |
||||
|
for (var comb of G.combination([1, 2, 3], 2)) { |
||||
|
answers.push(comb.slice()) |
||||
|
} |
||||
|
expect(answers).to.eql([...G.clone.combination([1, 2, 3], 2)]) |
||||
|
}) |
||||
|
|
||||
|
it('clones permutations', () => { |
||||
|
var answers = [] |
||||
|
for (var comb of G.permutation([1, 2, 3], 2)) { |
||||
|
answers.push(comb.slice()) |
||||
|
} |
||||
|
expect(answers).to.eql([...G.clone.permutation([1, 2, 3], 2)]) |
||||
|
|
||||
|
answers = [] |
||||
|
for (var comb of G.permutation([1, 2, 3])) { |
||||
|
answers.push(comb.slice()) |
||||
|
} |
||||
|
expect(answers).to.eql([...G.clone.permutation([1, 2, 3])]) |
||||
|
}) |
||||
|
|
||||
|
it('clones power sets', () => { |
||||
|
var answers = [] |
||||
|
for (var comb of G.powerSet([1, 2, 3])) { |
||||
|
answers.push(comb.slice()) |
||||
|
} |
||||
|
expect(answers).to.eql([...G.clone.powerSet([1, 2, 3])]) |
||||
|
}) |
||||
|
|
||||
|
it('clones permutations of combinations', () => { |
||||
|
var answers = [] |
||||
|
for (var comb of G.permutationCombination([1, 2, 3])) { |
||||
|
answers.push(comb.slice()) |
||||
|
} |
||||
|
expect(answers).to.eql([...G.clone.permutationCombination([1, 2, 3])]) |
||||
|
}) |
||||
|
|
||||
|
it('clones base N', () => { |
||||
|
var answers = [] |
||||
|
for (var comb of G.baseN([1, 2, 3])) { |
||||
|
answers.push(comb.slice()) |
||||
|
} |
||||
|
expect(answers).to.eql([...G.clone.baseN([1, 2, 3])]) |
||||
|
}) |
||||
|
|
||||
|
it('clones cartesian products', () => { |
||||
|
var answers = [] |
||||
|
for (var comb of G.cartesian([1, 2, 3], [5, 6], [1, 8])) { |
||||
|
answers.push(comb.slice()) |
||||
|
} |
||||
|
expect(answers).to.eql([...G.clone.cartesian([1, 2, 3], [5, 6], [1, 8])]) |
||||
|
}) |
||||
|
|
||||
|
}) |
@ -0,0 +1,27 @@ |
|||||
|
{ |
||||
|
"name": "js", |
||||
|
"lockfileVersion": 2, |
||||
|
"requires": true, |
||||
|
"packages": { |
||||
|
"": { |
||||
|
"dependencies": { |
||||
|
"generatorics": "^1.1.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/generatorics": { |
||||
|
"version": "1.1.0", |
||||
|
"resolved": "https://registry.npmjs.org/generatorics/-/generatorics-1.1.0.tgz", |
||||
|
"integrity": "sha1-aVBgu42IuQmzAXGlyz1CR2hmETg=", |
||||
|
"engines": { |
||||
|
"node": ">=6.0.0" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"generatorics": { |
||||
|
"version": "1.1.0", |
||||
|
"resolved": "https://registry.npmjs.org/generatorics/-/generatorics-1.1.0.tgz", |
||||
|
"integrity": "sha1-aVBgu42IuQmzAXGlyz1CR2hmETg=" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
{ |
||||
|
"dependencies": { |
||||
|
"generatorics": "^1.1.0" |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue