strips
Version:
Basic AI planning with STRIPS and PDDL.
391 lines (387 loc) • 12.2 kB
JavaScript
/*
* $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);