Last active
May 7, 2021 09:59
-
-
Save kybernetikos/11cccdf7cd6dd36b1506ee67e09bb46b to your computer and use it in GitHub Desktop.
Factoradic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const min = (a,b) => (a < b) ? a : b // this works on big ints too, unlike Math.min | |
const err = message => {throw new Error(message)} // useful if you want to throw an error in an expression | |
const castToNumber = bigInt => (BigInt(bigInt) <= Number.MAX_SAFE_INTEGER) ? Number(bigInt) : err(`Number too big: ${bigInt} > MAX_SAFE_INTEGER`) | |
const prefixTo = (array, length) => (array.length >= length) ? array : [...Array(length - array.length).fill(0), ...array] | |
const factorial = (() => { | |
const lookupTableSize = 20 | |
let lastVal = 1n | |
const lookUp = Array.from({length: lookupTableSize}, (_,i) => lastVal = i === 0 ? lastVal : BigInt(i) * lastVal) | |
return function factorial(n) { | |
let gotVal = BigInt(min(n, lookUp.length-1)) | |
let result = lookUp[gotVal] | |
while (gotVal < n) { result *= ++gotVal } | |
return result | |
} | |
})() | |
class Factoradic { | |
constructor(elements) { | |
if (elements[elements.length - 1] !== 0) { | |
// we could also check none of the place values are too large, but that would involve a fair bit of work. | |
throw new Error(`Not a valid factoradic number ${elements.join(":")}`) | |
} | |
this._elements = elements | |
} | |
toBigInt() { | |
return this._elements.reduceRight((prev, curr, i) => prev + factorial(this._elements.length - i - 1) * BigInt(curr), 0n) | |
} | |
permute(array) { | |
const fullElements = prefixTo(this._elements, array.length) | |
const valuesToAssign = [...array] | |
const result = [] | |
for (let place of fullElements) { | |
result.push(valuesToAssign.splice(castToNumber(place), 1)[0]) | |
} | |
return result | |
} | |
toString(separator = ":") { | |
return this._elements.join(separator) | |
} | |
static parse(string, separator = ":") { | |
return new Factoradic(string.split(separator).map(x => parseInt(x, 10))) | |
} | |
static permutations(array) { | |
if (array.length > 12) { | |
throw new Error(`Cannot generate all permutations of arrays with more than 12 elements because the resulting array would be too large for javascript. Your array had ${array.length} elements.`) | |
} | |
const _elements = [...array] | |
const length = castToNumber(factorial(_elements.length)) | |
return new Proxy([], { | |
get(target, prop) { | |
if (prop === 'length') { | |
return length | |
} else { | |
const numericProp = (typeof prop === "string") ? parseInt(prop, 10) : NaN | |
if (!isNaN(numericProp) && numericProp < length && numericProp >= 0 && prop == numericProp) { | |
return Factoradic.fromInt(numericProp).permute(_elements) | |
} else { | |
return target[prop] | |
} | |
} | |
} | |
}) | |
} | |
static fromInt(number) { | |
let maxPlace = 1 | |
while (factorial(maxPlace) <= number) { | |
maxPlace++ | |
} | |
const result = Array.from({length: maxPlace}) | |
let remainder = BigInt(number) | |
for (let i = 0; i < result.length; ++i) { | |
let placeVal = factorial(maxPlace - i - 1) | |
let digit = remainder / placeVal | |
remainder -= digit * placeVal | |
result[i] = castToNumber(digit) | |
} | |
return new Factoradic(result) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const source = ["alice", "bob", "claudia", "dave"] | |
const srcPerms = Factoradic.permutations(source) | |
for (let permutation of srcPerms) { | |
console.log(permutation) | |
} | |
const luckyPermutationNo = Math.floor(Math.random()*srcPerms.length) | |
console.log(`Lucky permutation is number ${luckyPermutationNo} : ${srcPerms[luckyPermutationNo]}`) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment