UNPKG

8.8 kBJavaScriptView Raw
1/*
2 * $Id: combinatorics.js,v 0.25 2013/03/11 15:42:14 dankogai Exp dankogai $
3 *
4 * Licensed under the MIT license.
5 * http://www.opensource.org/licenses/mit-license.php
6 *
7 * References:
8 * http://www.ruby-doc.org/core-2.0/Array.html#method-i-combination
9 * http://www.ruby-doc.org/core-2.0/Array.html#method-i-permutation
10 * http://en.wikipedia.org/wiki/Factorial_number_system
11 */
12(function(global) {
13 'use strict';
14 if (global.Combinatorics) return;
15 /* combinatory arithmetics */
16 var P = function(m, n) {
17 var t, p = 1;
18 if (m < n) {
19 t = m;
20 m = n;
21 n = t;
22 }
23 while (n--) p *= m--;
24 return p;
25 };
26 var C = function(m, n) {
27 return P(m, n) / P(n, n);
28 };
29 var factorial = function(n) {
30 return P(n, n);
31 };
32 var factoradic = function(n, d) {
33 var f = 1;
34 if (!d) {
35 for (d = 1; f < n; f *= ++d);
36 if (f > n) f /= d--;
37 } else {
38 f = factorial(d);
39 }
40 var result = [0];
41 for (; d; f /= d--) {
42 result[d] = Math.floor(n / f);
43 n %= f;
44 }
45 return result;
46 };
47 /* common methods */
48 var addProperties = function(dst, src) {
49 Object.keys(src).forEach(function(p) {
50 Object.defineProperty(dst, p, {
51 value: src[p]
52 });
53 });
54 };
55 var hideProperty = function(o, p) {
56 Object.defineProperty(o, p, {
57 writable: true
58 });
59 };
60 var toArray = function(f) {
61 var e, result = [];
62 this.init();
63 while (e = this.next()) result.push(f ? f(e) : e);
64 this.init();
65 return result;
66 };
67 var common = {
68 toArray: toArray,
69 map: toArray,
70 forEach: function(f) {
71 var e;
72 this.init();
73 while (e = this.next()) f(e);
74 this.init();
75 },
76 filter: function(f) {
77 var e, result = [];
78 this.init();
79 while (e = this.next()) if (f(e)) result.push(e);
80 this.init();
81 return result;
82 }
83
84 };
85 /* power set */
86 var power = function(ary, fun) {
87 if (ary.length > 32) throw new RangeError;
88 var size = 1 << ary.length,
89 sizeOf = function() {
90 return size;
91 },
92 that = Object.create(ary.slice(), {
93 length: {
94 get: sizeOf
95 }
96 });
97 hideProperty(that, 'index');
98 addProperties(that, {
99 valueOf: sizeOf,
100 init: function() {
101 that.index = 0;
102 },
103 nth: function(n) {
104 if (n >= size) return;
105 var i = 0,
106 result = [];
107 for (; n; n >>>= 1, i++) if (n & 1) result.push(this[i]);
108 return result;
109 },
110 next: function() {
111 return this.nth(this.index++);
112 }
113 });
114 addProperties(that, common);
115 that.init();
116 return (typeof (fun) === 'function') ? that.map(fun) : that;
117 };
118 /* combination */
119 var nextIndex = function(n) {
120 var smallest = n & -n,
121 ripple = n + smallest,
122 new_smallest = ripple & -ripple,
123 ones = ((new_smallest / smallest) >> 1) - 1;
124 return ripple | ones;
125 };
126 var combination = function(ary, nelem, fun) {
127 if (ary.length > 32) throw new RangeError;
128 if (!nelem) nelem = ary.length;
129 if (nelem < 1) throw new RangeError;
130 if (nelem > ary.length) throw new RangeError;
131 var first = (1 << nelem) - 1,
132 size = C(ary.length, nelem),
133 maxIndex = 1 << ary.length,
134 sizeOf = function() {
135 return size;
136 },
137 that = Object.create(ary.slice(), {
138 length: {
139 get: sizeOf
140 }
141 });
142 hideProperty(that, 'index');
143 addProperties(that, {
144 valueOf: sizeOf,
145 init: function() {
146 this.index = first;
147 },
148 next: function() {
149 if (this.index >= maxIndex) return;
150 var i = 0,
151 n = this.index,
152 result = [];
153 for (; n; n >>>= 1, i++) if (n & 1) result.push(this[i]);
154 this.index = nextIndex(this.index);
155 return result;
156 }
157 });
158 addProperties(that, common);
159 that.init();
160 return (typeof (fun) === 'function') ? that.map(fun) : that;
161 };
162 /* permutation */
163 var _permutation = function(ary) {
164 var that = ary.slice(),
165 size = factorial(that.length);
166 that.index = 0;
167 that.next = function() {
168 if (this.index >= size) return;
169 var copy = this.slice(),
170 digits = factoradic(this.index, this.length),
171 result = [],
172 i = this.length - 1;
173 for (; i >= 0; --i) result.push(copy.splice(digits[i], 1)[0]);
174 this.index++;
175 return result;
176 };
177 return that;
178 };
179 // which is really a permutation of combination
180 var permutation = function(ary, nelem, fun) {
181 if (!nelem) nelem = ary.length;
182 if (nelem < 1) throw new RangeError;
183 if (nelem > ary.length) throw new RangeError;
184 var size = P(ary.length, nelem),
185 sizeOf = function() {
186 return size;
187 },
188 that = Object.create(ary.slice(), {
189 length: {
190 get: sizeOf
191 }
192 });
193 hideProperty(that, 'cmb');
194 hideProperty(that, 'per');
195 addProperties(that, {
196 valueOf: function() {
197 return size;
198 },
199 init: function() {
200 this.cmb = combination(ary, nelem);
201 this.per = _permutation(this.cmb.next());
202 },
203 next: function() {
204 var result = this.per.next();
205 if (!result) {
206 var cmb = this.cmb.next();
207 if (!cmb) return;
208 this.per = _permutation(cmb);
209 return this.next();
210 }
211 return result;
212 }
213 });
214 addProperties(that, common);
215 that.init();
216 return (typeof (fun) === 'function') ? that.map(fun) : that;
217 };
218 /* Cartesian Product */
219 var arraySlice = Array.prototype.slice;
220 var cartesianProduct = function() {
221 if (!arguments.length) throw new RangeError;
222 var args = arraySlice.call(arguments),
223 size = args.reduce(function(p, a) {
224 return p * a.length;
225 }, 1),
226 sizeOf = function() {
227 return size;
228 },
229 dim = args.length,
230 that = Object.create(args, {
231 length: {
232 get: sizeOf
233 }
234 });
235 if (!size) throw new RangeError;
236 hideProperty(that, 'index');
237 addProperties(that, {
238 valueOf: sizeOf,
239 dim: dim,
240 init: function() {
241 this.index = 0;
242 },
243 get: function() {
244 if (arguments.length !== this.length) return;
245 var result = [],
246 d = 0;
247 for (; d < dim; d++) {
248 var i = arguments[d];
249 if (i >= this[d].length) return;
250 result.push(this[d][i]);
251 }
252 return result;
253 },
254 nth: function(n) {
255 var result = [],
256 d = 0;
257 for (; d < dim; d++) {
258 var l = this[d].length;
259 var i = n % l;
260 result.push(this[d][i]);
261 n -= i;
262 n /= l;
263 }
264 return result;
265 },
266 next: function() {
267 if (this.index >= size) return;
268 var result = this.nth(this.index);
269 this.index++;
270 return result;
271 }
272 });
273 addProperties(that, common);
274 that.init();
275 return that;
276 };
277 /* export */
278 addProperties(global.Combinatorics = Object.create(null), {
279 C: C,
280 P: P,
281 factorial: factorial,
282 factoradic: factoradic,
283 cartesianProduct: cartesianProduct,
284 combination: combination,
285 permutation: permutation,
286 power: power
287 });
288})(this);