UNPKG

strips

Version:

Basic AI planning with STRIPS and PDDL.

391 lines (387 loc) 12.2 kB
/* * $Id: combinatorics.js,v 0.25 2013/03/11 15:42:14 dankogai Exp dankogai $ * * 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 * http://en.wikipedia.org/wiki/Factorial_number_system */ (function(global) { 'use strict'; if (global.Combinatorics) return; var version = "0.4.0"; /* combinatory arithmetics */ var P = function(m, n) { var t, p = 1; if (m < n) { t = m; m = n; n = t; } while (n--) p *= m--; return p; }; var C = function(m, n) { return P(m, n) / P(n, n); }; var factorial = function(n) { return P(n, n); }; var factoradic = function(n, d) { var f = 1; if (!d) { for (d = 1; f < n; f *= ++d); if (f > n) f /= d--; } else { f = factorial(d); } var result = [0]; for (; d; f /= d--) { result[d] = Math.floor(n / f); n %= f; } return result; }; /* common methods */ var addProperties = function(dst, src) { Object.keys(src).forEach(function(p) { Object.defineProperty(dst, p, { value: src[p] }); }); }; var hideProperty = function(o, p) { Object.defineProperty(o, p, { writable: true }); }; var toArray = function(f) { var e, result = []; this.init(); while (e = this.next()) result.push(f ? f(e) : e); this.init(); return result; }; var common = { toArray: toArray, map: toArray, forEach: function(f) { var e; this.init(); while (e = this.next()) f(e); this.init(); }, filter: function(f) { var e, result = []; this.init(); while (e = this.next()) if (f(e)) result.push(e); this.init(); return result; } }; /* power set */ var power = function(ary, fun) { if (ary.length > 32) throw new RangeError; var size = 1 << ary.length, sizeOf = function() { return size; }, that = Object.create(ary.slice(), { length: { get: sizeOf } }); hideProperty(that, 'index'); addProperties(that, { valueOf: sizeOf, init: function() { that.index = 0; }, nth: function(n) { if (n >= size) return; var i = 0, result = []; for (; n; n >>>= 1, i++) if (n & 1) result.push(this[i]); return result; }, next: function() { return this.nth(this.index++); } }); addProperties(that, common); that.init(); return (typeof (fun) === 'function') ? that.map(fun) : that; }; /* combination */ var nextIndex = function(n) { var smallest = n & -n, ripple = n + smallest, new_smallest = ripple & -ripple, ones = ((new_smallest / smallest) >> 1) - 1; return ripple | ones; }; var combination = function(ary, nelem, fun) { //if (ary.length > 32) throw new RangeError; if (!nelem) nelem = ary.length; if (nelem < 1) throw new RangeError; if (nelem > ary.length) throw new RangeError; var first = (1 << nelem) - 1, size = C(ary.length, nelem), maxIndex = 1 << ary.length, sizeOf = function() { return size; }, that = Object.create(ary.slice(), { length: { get: sizeOf } }); hideProperty(that, 'index'); addProperties(that, { valueOf: sizeOf, init: function() { this.index = first; }, next: function() { if (this.index >= maxIndex) return; var i = 0, n = this.index, result = []; for (; n; n >>>= 1, i++) if (n & 1) result.push(this[i]); this.index = nextIndex(this.index); return result; } }); addProperties(that, common); that.init(); return (typeof (fun) === 'function') ? that.map(fun) : that; }; /* permutation */ var _permutation = function(ary) { var that = ary.slice(), size = factorial(that.length); that.index = 0; that.next = function() { if (this.index >= size) return; var copy = this.slice(), digits = factoradic(this.index, this.length), result = [], i = this.length - 1; for (; i >= 0; --i) result.push(copy.splice(digits[i], 1)[0]); this.index++; return result; }; return that; }; // which is really a permutation of combination var permutation = function(ary, nelem, fun) { if (!nelem) nelem = ary.length; if (nelem < 1) throw new RangeError; if (nelem > ary.length) throw new RangeError; var size = P(ary.length, nelem), sizeOf = function() { return size; }, that = Object.create(ary.slice(), { length: { get: sizeOf } }); hideProperty(that, 'cmb'); hideProperty(that, 'per'); addProperties(that, { valueOf: function() { return size; }, init: function() { this.cmb = combination(ary, nelem); this.per = _permutation(this.cmb.next()); }, next: function() { var result = this.per.next(); if (!result) { var cmb = this.cmb.next(); if (!cmb) return; this.per = _permutation(cmb); return this.next(); } return result; } }); addProperties(that, common); that.init(); return (typeof (fun) === 'function') ? that.map(fun) : that; }; var PC = function(m) { var total = 0; for (var n = 1; n <= m; n++) { var p = P(m,n); total += p; }; return total; }; // which is really a permutation of combination var permutationCombination = function(ary, fun) { // if (!nelem) nelem = ary.length; // if (nelem < 1) throw new RangeError; // if (nelem > ary.length) throw new RangeError; var size = PC(ary.length), sizeOf = function() { return size; }, that = Object.create(ary.slice(), { length: { get: sizeOf } }); hideProperty(that, 'cmb'); hideProperty(that, 'per'); hideProperty(that, 'nelem'); addProperties(that, { valueOf: function() { return size; }, init: function() { this.nelem = 1; // console.log("Starting nelem: " + this.nelem); this.cmb = combination(ary, this.nelem); this.per = _permutation(this.cmb.next()); }, next: function() { var result = this.per.next(); if (!result) { var cmb = this.cmb.next(); if (!cmb) { this.nelem++; // console.log("increment nelem: " + this.nelem + " vs " + ary.length); if (this.nelem > ary.length) return; this.cmb = combination(ary, this.nelem); cmb = this.cmb.next(); if (!cmb) return; } this.per = _permutation(cmb); return this.next(); } return result; } }); addProperties(that, common); that.init(); return (typeof (fun) === 'function') ? that.map(fun) : that; }; /* Cartesian Product */ var arraySlice = Array.prototype.slice; var cartesianProduct = function() { if (!arguments.length) throw new RangeError; var args = arraySlice.call(arguments), size = args.reduce(function(p, a) { return p * a.length; }, 1), sizeOf = function() { return size; }, dim = args.length, that = Object.create(args, { length: { get: sizeOf } }); if (!size) throw new RangeError; hideProperty(that, 'index'); addProperties(that, { valueOf: sizeOf, dim: dim, init: function() { this.index = 0; }, get: function() { if (arguments.length !== this.length) return; var result = [], d = 0; for (; d < dim; d++) { var i = arguments[d]; if (i >= this[d].length) return; result.push(this[d][i]); } return result; }, nth: function(n) { var result = [], d = 0; for (; d < dim; d++) { var l = this[d].length; var i = n % l; result.push(this[d][i]); n -= i; n /= l; } return result; }, next: function() { if (this.index >= size) return; var result = this.nth(this.index); this.index++; return result; } }); addProperties(that, common); that.init(); return that; }; /* baseN */ var baseN = function(ary, nelem, fun) { if (!nelem) nelem = ary.length; if (nelem < 1) throw new RangeError; var base = ary.length, size = Math.pow(base, nelem); if (size > Math.pow(2,32)) throw new RangeError; var sizeOf = function() { return size; }, that = Object.create(ary.slice(), { length: { get: sizeOf } }); hideProperty(that, 'index'); addProperties(that, { valueOf: sizeOf, init: function() { that.index = 0; }, nth: function(n) { if (n >= size) return; var result = []; for (var i = 0; i < nelem; i++) { var d = n % base; result.push(ary[d]) n -= d; n /= base } return result; }, next: function() { return this.nth(this.index++); } }); addProperties(that, common); that.init(); return (typeof (fun) === 'function') ? that.map(fun) : that; }; /* export */ addProperties(global.Combinatorics = Object.create(null), { C: C, P: P, factorial: factorial, factoradic: factoradic, cartesianProduct: cartesianProduct, combination: combination, permutation: permutation, permutationCombination: permutationCombination, power: power, baseN: baseN, VERSION: version }); })(this);