Skip to content

Commit 595e153

Browse files
author
Nikos M
committed
v.0.7.5
* multiset opermutations (almost complete, rank/unrank methods missing) * subset2binary, combination2binary use MSB to LSB order (expected) * minor changes
1 parent 8a5d9fd commit 595e153

22 files changed

+2065
-472
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ A combinatorics library for Node/XPCOM/JS, PHP, Python, C/C++, Java
55
(php/python/java/c implementations in progress)
66

77

8-
**version 0.7.0** (~ 38kB minified, ~ 12kB zipped)
8+
**version 0.7.5** (~ 40kB minified, ~ 12kB zipped)
99

1010
![abacus combinatorial numbers](/abacus.jpg)
1111

@@ -58,7 +58,7 @@ A combinatorics library for Node/XPCOM/JS, PHP, Python, C/C++, Java
5858
* `Tuple` (`test/tuples.js`)
5959
* `Permutation` (`test/permutations.js`, `test/permutations-bigint.js`)
6060
* `CyclicPermutation` (`test/cyclic_permutations.js`)
61-
* `MultisetPermutation` (`test/multiset_permutations.js`) **todo**
61+
* `MultisetPermutation` (`test/multiset_permutations.js`) **almost complete**
6262
* `DerangementPermutation` (`test/derangements.js`) **almost complete**
6363
* `InvolutionPermutation` (`test/involutions.js`) **todo**
6464
* `UnorderedCombination` (`test/combinations.js`)

src/js/Abacus.js

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
*
33
* Abacus
44
* A combinatorics library for Node/XPCOM/JS, PHP, Python
5-
* @version: 0.7.0
5+
* @version: 0.7.5
66
* https://github.com/foo123/Abacus
77
**/
88
!function( root, name, factory ){
@@ -22,7 +22,7 @@ else if ( !(name in root) ) /* Browser/WebWorker/.. */
2222
/* module factory */ function ModuleFactory__Abacus( undef ){
2323
"use strict";
2424

25-
var Abacus = {VERSION: "0.7.0"}, PROTO = 'prototype', CLASS = 'constructor'
25+
var Abacus = {VERSION: "0.7.5"}, PROTO = 'prototype', CLASS = 'constructor'
2626
,slice = Array.prototype.slice, HAS = Object[PROTO].hasOwnProperty, toString = Object[PROTO].toString
2727
,trim_re = /^\s+|\s+$/g
2828
,trim = String.prototype.trim
@@ -663,14 +663,14 @@ function subset2binary( item, n )
663663
{
664664
if ( 0 > n ) return [];
665665
var binary = array(n, 0, 0), i, l = item.length;
666-
for(i=0; i<l; i++) binary[item[i]] = 1;
666+
for(n=n-1,i=0; i<l; i++) binary[n-item[i]] = 1;
667667
return binary;
668668
}
669669
function binary2subset( item, n )
670670
{
671671
n = stdMath.min(n||item.length, item.length);
672672
var subset = [], i;
673-
for(i=0; i<n; i++) if ( 0 < item[i] ) subset.push(i);
673+
for(n=n-1,i=0; i<=n; i++) if ( 0 < item[i] ) subset.push(n-i);
674674
return subset;
675675
}
676676
function conjugatepartition( partition, packed )
@@ -1090,19 +1090,20 @@ function next_permutation( item, N, dir, type )
10901090
//else last item
10911091
else item = null;
10921092
}
1093-
else//if ( "derangement" === type || "permutation" === type )
1093+
else//if ( ("multiset" === type) || ("derangement" === type) || ("permutation" === type) )
10941094
{
10951095
do{
10961096
fixed = false;
10971097
//Find the largest index k such that a[k] > a[k + 1].
1098+
// taking into account equal elements, generates multiset permutations
10981099
k = n-2;
1099-
while((k >= 0) && (item[k] < item[k+1])) k--;
1100+
while((k >= 0) && (item[k] <= item[k+1])) k--;
11001101
// If no such index exists, the permutation is the last permutation.
11011102
if ( k >=0 )
11021103
{
11031104
//Find the largest index kl greater than k such that a[k] > a[kl].
11041105
kl = n-1;
1105-
while (kl>k && item[k]<item[kl]) kl--;
1106+
while (kl>k && item[k]<=item[kl]) kl--;
11061107
//Swap the value of a[k] with that of a[l].
11071108
s = item[k]; item[k] = item[kl]; item[kl] = s;
11081109
//Reverse the sequence from a[k + 1] up to and including the final element a[n].
@@ -1138,19 +1139,20 @@ function next_permutation( item, N, dir, type )
11381139
//else last item
11391140
else item = null;
11401141
}
1141-
else//if ( "derangement" === type || "permutation" === type )
1142+
else//if ( ("multiset" === type) || ("derangement" === type) || ("permutation" === type) )
11421143
{
11431144
do{
11441145
fixed = false;
11451146
//Find the largest index k such that a[k] < a[k + 1].
1147+
// taking into account equal elements, generates multiset permutations
11461148
k = n-2;
1147-
while((k >= 0) && (item[k] > item[k+1])) k--;
1149+
while((k >= 0) && (item[k] >= item[k+1])) k--;
11481150
// If no such index exists, the permutation is the last permutation.
11491151
if ( k >=0 )
11501152
{
11511153
//Find the largest index kl greater than k such that a[k] < a[kl].
11521154
kl = n-1;
1153-
while (kl>k && item[k]>item[kl]) kl--;
1155+
while (kl>k && item[k]>=item[kl]) kl--;
11541156
//Swap the value of a[k] with that of a[l].
11551157
s = item[k]; item[k] = item[kl]; item[kl] = s;
11561158
//Reverse the sequence from a[k + 1] up to and including the final element a[n].
@@ -2493,7 +2495,8 @@ Tensor = Abacus.Tensor = Class(CombinatorialIterator, {
24932495
,T: CombinatorialIterator.T
24942496

24952497
,count: function( n, $ ) {
2496-
return $ && "tuple"===$.type ? (!n || !n[0] ? 0 : Abacus.Math.exp(n[0], n[1])) : (!n || !n.length ? 0 : Abacus.Math.product(n));
2498+
var O = Abacus.Arithmetic.O;
2499+
return $ && "tuple"===$.type ? (!n || (0 >= n[0]) ? O : Abacus.Math.exp(n[0], n[1])) : (!n || !n.length ? O : Abacus.Math.product(n));
24972500
}
24982501
,initial: function( dir, n, $ ) {
24992502
// last (0>dir) is C-symmetric of first (0<dir)
@@ -2605,33 +2608,48 @@ Permutation = Abacus.Permutation = Class(CombinatorialIterator, {
26052608
constructor: function Permutation( n, $ ) {
26062609
var self = this;
26072610
if ( !(self instanceof Permutation) ) return new Permutation(n, $);
2608-
$ = $ || {}; $.type = $.type || "permutation";
2611+
$ = $ || {}; $.type = String($.type || "permutation").toLowerCase();
26092612
n = n||1;
26102613
if ( n instanceof CombinatorialIterator )
26112614
{
26122615
$.sub = n;
26132616
n = $.sub.dimension();
26142617
}
26152618
$.base = $.dimension = n;
2616-
// random ordering for derangements is based on random generation, instead of random unranking
2617-
$.rand = {"derangement":1,"involution":1};
2619+
// random ordering for multisets / derangements / involutions
2620+
// is based on random generation, instead of random unranking
2621+
$.rand = {"multiset":1,"derangement":1,"involution":1};
2622+
if ( "multiset" === $.type )
2623+
$.multiplicity = is_array($.multiplicity) && $.multiplicity.length ? $.multiplicity.slice() : array(n, 1, 0);
26182624
CombinatorialIterator.call(self, "Permutation", n, $);
26192625
}
26202626

26212627
,__static__: {
2622-
C: function( item, n ) {
2623-
return conjugation( item, -1 );
2624-
}
2628+
C: CombinatorialIterator.C
26252629
,P: CombinatorialIterator.P
26262630
,T: CombinatorialIterator.T
26272631

26282632
,count: function( n, $ ) {
2629-
var type = $ && $.type ? $.type : "permutation";
2630-
return "cyclic" === type ? Abacus.Arithmetic.N(n) : Abacus.Math.factorial(n, "derangement"===type?false:null);
2633+
var O = Abacus.Arithmetic.O,
2634+
factorial = Abacus.Math.factorial,
2635+
type = $ && $.type ? $.type : "permutation";
2636+
if ( 0 >= n )
2637+
return O;
2638+
else if ( "cyclic" === type )
2639+
return Abacus.Arithmetic.N(n);
2640+
else if ( "multiset" === type )
2641+
return factorial(n, $.multiplicity);
2642+
else if ( "derangement" === type )
2643+
return 2 > n ? O : factorial(n, false);
2644+
else if ( "involution" === type )
2645+
return O;
2646+
else//if ( "permutation" === type )
2647+
return factorial(n);
26312648
}
26322649
,initial: function( dir, n, $ ) {
26332650
// last (0>dir) is C-symmetric of first (0<dir)
26342651
var item, type = $ && $.type ? $.type : "permutation";
2652+
if ( 0 >= n ) return null;
26352653
if ( "cyclic" === type )
26362654
{
26372655
item = 0 > dir ? [n-1].concat(array(n-1, 0, 1)) : array(n, 0, 1);
@@ -2649,17 +2667,41 @@ Permutation = Abacus.Permutation = Class(CombinatorialIterator, {
26492667
item = 0 > dir ? array(n, n-1, -1) : array(n, function(i){return i&1?i-1:i+1;});
26502668
}
26512669
}
2670+
else if ( "multiset" === type )
2671+
{
2672+
var m = $.multiplicity, nm = m.length, ki = 0, k,
2673+
dk = 1, k0 = 0, mk = ki < nm ? m[ki] : 1;
2674+
if ( 0 > dir ) { dk = -1; k0 = nm-1; }
2675+
k = k0;
2676+
item = array(n, function(){
2677+
if ( 0 >= mk ) { ki++; k+=dk; mk = ki<nm ? m[ki] : 1; }
2678+
mk--;
2679+
return k;
2680+
});
2681+
}
26522682
else//if ( ("involution" === type) || ("multiset" === type) || ("permutation" === type) )
26532683
{
26542684
item = 0 > dir ? array(n, n-1, -1) : array(n, 0, 1);
26552685
}
26562686
return item;
26572687
}
26582688
,cascade: CombinatorialIterator.cascade
2659-
,dual: CombinatorialIterator.dual
2689+
,dual: function( item, index, n, $ ) {
2690+
if ( null == item ) return null;
2691+
// some C-P-T processes at play here
2692+
var klass = this, type = $ && $.type ? $.type : "permutation",
2693+
order = $ && $.order ? $.order : 0,
2694+
nm = "multiset" === type ? $.multiplicity.length : n,
2695+
C = klass.C, P = klass.P, T = klass.T;
2696+
if ( RANDOM & order ) item = REFLECTED & order ? P( item, n ) : item.slice( );
2697+
else if (MINIMAL & order ) item = REFLECTED & order ? P( item, n ) : item.slice( );
2698+
else if ( COLEX & order ) item = REFLECTED & order ? C( item, nm ) : P( C( item, nm ), n );
2699+
else/*if ( LEX & order )*/item = REFLECTED & order ? P( item, n ) : item.slice( );
2700+
return item;
2701+
}
26602702
,succ: function( dir, item, index, n, $ ) {
26612703
var type = $ && $.type ? $.type : "permutation";
2662-
if ( ("involution" === type) || ("multiset" === type) ) return null;
2704+
if ( "involution" === type ) return null;
26632705
return next_permutation( item, n, dir, type );
26642706
}
26652707
,rand: function( n, $ ) {
@@ -2697,7 +2739,17 @@ Permutation = Abacus.Permutation = Class(CombinatorialIterator, {
26972739
}while( fixed );
26982740
return item;
26992741
}
2700-
else if ( ("involution" === type) || ("multiset" === type) )
2742+
else if ( "multiset" === type )
2743+
{
2744+
// p ~ m1!*..*mk! / n!
2745+
var m = $.multiplicity, nm = m.length, k = 0, mk = m[k];
2746+
return shuffle(array(n, function(){
2747+
if ( 0 >= mk ) { k++; mk = k < nm ? m[k] : 1; }
2748+
mk--;
2749+
return k;
2750+
}));
2751+
}
2752+
else if ( "involution" === type )
27012753
{
27022754
return NotImplemented();
27032755
}
@@ -2881,15 +2933,16 @@ Combination = Abacus.Combination = Class(CombinatorialIterator, {
28812933
,T: CombinatorialIterator.T
28822934

28832935
,count: function( n, $ ) {
2884-
var type = $ && $.type ? $.type : "unordered";
2936+
var factorial = Abacus.Math.factorial,
2937+
type = $ && $.type ? $.type : "unordered";
28852938
return "ordered+repeated" === type ? (
28862939
Abacus.Math.exp(n[0], n[1])
28872940
) : ("repeated" === type ? (
2888-
Abacus.Math.factorial(n[0]+n[1]-1, n[1])
2941+
factorial(n[0]+n[1]-1, n[1])
28892942
) : ("ordered" === type ? (
2890-
Abacus.Math.factorial(n[0], -n[1])
2943+
factorial(n[0], -n[1])
28912944
) : (
2892-
Abacus.Math.factorial(n[0], n[1])
2945+
factorial(n[0], n[1])
28932946
)));
28942947
}
28952948
,initial: function( dir, n, $ ) {

src/js/Abacus.min.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/combinations.txt

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Abacus.Combinations (VERSION = 0.7.0)
1+
Abacus.Combinations (VERSION = 0.7.5)
22
---
33
o = Abacus.Combination(6,3)
44
o.dimension()
@@ -161,28 +161,28 @@ o.order("colex,reversed")
161161
[ 0, 1, 3 ]
162162
[ 0, 1, 2 ]
163163
o.order("random")
164-
[ 0, 2, 3 ]
165-
[ 0, 2, 4 ]
166-
[ 1, 3, 4 ]
167-
[ 1, 3, 5 ]
168-
[ 0, 1, 3 ]
169-
[ 0, 2, 5 ]
170-
[ 1, 2, 3 ]
171-
[ 2, 3, 5 ]
172-
[ 0, 1, 5 ]
173-
[ 0, 3, 4 ]
174-
[ 2, 4, 5 ]
175-
[ 0, 1, 4 ]
176164
[ 0, 1, 2 ]
177165
[ 0, 4, 5 ]
166+
[ 0, 2, 4 ]
167+
[ 0, 2, 5 ]
178168
[ 0, 3, 5 ]
179-
[ 3, 4, 5 ]
169+
[ 1, 3, 5 ]
170+
[ 2, 4, 5 ]
171+
[ 0, 3, 4 ]
180172
[ 1, 2, 4 ]
181-
[ 1, 4, 5 ]
182173
[ 1, 2, 5 ]
174+
[ 1, 2, 3 ]
175+
[ 0, 2, 3 ]
176+
[ 1, 3, 4 ]
177+
[ 0, 1, 4 ]
178+
[ 1, 4, 5 ]
183179
[ 2, 3, 4 ]
180+
[ 2, 3, 5 ]
181+
[ 0, 1, 3 ]
182+
[ 3, 4, 5 ]
183+
[ 0, 1, 5 ]
184184
o.random()
185-
[ 2, 4, 5 ]
185+
[ 0, 1, 5 ]
186186
o.order("colex").range(-5, -1)
187187
[ 2, 3, 5 ]
188188
[ 0, 4, 5 ]

0 commit comments

Comments
 (0)