Skip to content

Commit

Permalink
v.0.9.4 contd
Browse files Browse the repository at this point in the history
* implement GCD (Euclidean algorithm) generic using Arithmetic
* generate pan-diagonal latin squares when possible, else default to normal latin squares
* add examples of combonatorial solutions of popular problems (eg n-queens) with sample code and notes
* update readme, tests
  • Loading branch information
foo123 committed May 21, 2019
1 parent 183247a commit 33e83ff
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 18 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ A combinatorics library for Node.js / Browser / XPCOM Javascript, PHP, Python, C

* [Live Playground Example](https://foo123.github.io/examples/abacus)
* [Features](#features)
* [Combinatorial Examples](https://github.com/foo123/Abacus/master/examples/README.md)
* [Performance](#performance)
* [Credits and References](#credits-and-references)
* [Example API](#example-api)
Expand Down
107 changes: 107 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Combinatorial Solutions to Popular Problems using Abacus


The solutions, to popular problems, below exhibit the combinatorial capabilities of solving complex combinatorial problems of `Abacus`. Sometimes a solution is found by exhaustive search, other times better than full exhaustive search can be achieved and other times the solution does not require search at all but simply smart composition and manipulation of appropriate combinatorial objects.


### Contents

* [N-Queens Problem](#n-queens)
* [Knapsack Problem](#knapsack)
* [TSP Problem](#tsp)


### N-Queens

[N-Queens Problem, wikipedia](https://en.wikipedia.org/wiki/Eight_queens_puzzle)

see associated file: `examples/n_queens.js`

**Exhaustive Search**

Let's assume we have some utility methods which allow us to check if a certain potential solution configuration is valid `isValid` and also (we need that as well) a method (`toRowColumn`) to map numeric patterns of combinatorial objects to `(row,column)` pairs on a hypothetical `NxN` grid.

With these utitlities available we can start directly using an exhaustive search among all configurations of placing `N` queens on distinct positions on an `NxN` grid.


```javascript
o = Abacus.Combination(N*N, N, {output:toRowColumn}).filterBy(isValid);

echo(''+N+' Queens solutions (exhaustive search): START');
for(let solution of o) echo(solution);
echo(''+N+' Queens solutions (exhaustive search): END');
```

The above (for `N=4`) gives the following output:

```text
4 Queens solutions (exhaustive search): START
[ [ 0, 1 ], [ 1, 3 ], [ 2, 0 ], [ 3, 2 ] ]
[ [ 0, 2 ], [ 1, 0 ], [ 2, 3 ], [ 3, 1 ] ]
4 Queens solutions (exhaustive search): END
```

**Reduced Exhaustive Search**

However searching among all combinations is inefficient, we can be a little smarter and assume from the beginning that each queen is placed on different row (or column). Then we simply check among all permutations of assigning each queen on a specific (different) column.

```javascript
o = Abacus.Permutation(N, {output:toRowColumnP}).filterBy(isValid);

echo(''+N+' Queens solutions (reduced exhaustive search): START');
for(let solution of o) echo(solution);
echo(''+N+' Queens solutions (reduced exhaustive search): END');
```
The above (for `N=4`) gives the following output:

```text
4 Queens solutions (reduced exhaustive search): START
[ [ 0, 1 ], [ 1, 3 ], [ 2, 0 ], [ 3, 2 ] ]
[ [ 0, 2 ], [ 1, 0 ], [ 2, 3 ], [ 3, 1 ] ]
4 Queens solutions (reduced exhaustive search): END
```

**Exploiting Symmetries**

If we only need to find one solution, then there is an interesting connection between **pan-diagonal latin/magic squares** and **n-queens problems**. Specificaly if we have a pan-diagonal latin or magic square of order `N` then we can have (at least) one solution for the `N` Queens problem simply by placing a queen on each cell which contains only the symbol/number `s` (whatever we choose to be).

Since `Abacus` can generate `LatinSquares` and also will try to generate pan-diagonal latin squares if possible (for example for `N=5` it is possible), then we can generate a solution to the 5-Queens problem as follows:

```javascript
o = Abacus.LatinSquare.make(N);
echo(''+N+' Queens solution (pan-diagonal latin square): START');
o.forEach(function(oi){echo(oi.map(function(x){ return N===x ? 'X' : 'O'}).join(' '));});
echo(''+N+' Queen solutions (pan-diagonal latin square): END');
```

For `N=5` we get the following output:

```text
5 Queens solution (pan-diagonal latin square): START
O O X O O
O O O O X
O X O O O
O O O X O
X O O O O
5 Queen solutions (pan-diagonal latin square): END
```

We saw how we can explore and solve the N-Queens problem with a few lines of code using `Abacus` library.


### Knapsack

[Knapsack Problem, wikipedia](https://en.wikipedia.org/wiki/Knapsack_problem)

see associated file: `examples/knapsack.js`

**to be added**


### TSP

[Travelling Salesman Problem, wikipedia](https://en.wikipedia.org/wiki/Travelling_salesman_problem)

see associated file: `examples/tsp.js`

**to be added**
7 changes: 7 additions & 0 deletions examples/knapsack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
var isNode = 'undefined' !== typeof global && '[object global]' === {}.toString.call(global);
var Abacus = isNode ? require('../src/js/Abacus.js') : window.Abacus, echo = console.log;

var o, N;
// solve exactly the Knapsack problem by Combinatorial methods using Abacus

// to do
73 changes: 73 additions & 0 deletions examples/n_queens.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
var isNode = 'undefined' !== typeof global && '[object global]' === {}.toString.call(global);
var Abacus = isNode ? require('../src/js/Abacus.js') : window.Abacus, echo = console.log;

var o, N;
// solve exactly the N-queens problem by Combinatorial methods using Abacus

// utility functions
function toRowColumn(item)
{
// convert num in 0..N^2-1 to associated row and column in NxN grid
var N = item.length, i, output = new Array(N);
for(i=0; i<N; i++) output[i] = [~~(item[i] / N)/* associated row*/, item[i] % N/* associated column*/];
return output;
}
function toRowColumnP(item)
{
// convert permutation of N queens each on different row of NxN grid
var N = item.length, i, output = new Array(N);
for(i=0; i<N; i++) output[i] = [i/* different row */, item[i]/* permutation of column*/];
return output;
}
function isValid(solution)
{
var N = solution.length, i, j;
for(i=0; i<N; i++)
{

for(j=0; j<N; j++)
{
if ( i === j ) continue;
if ( solution[i][0]===solution[j][0] /* same row */ || solution[i][1]===solution[j][1] /* same column */ || solution[i][0]-solution[i][1]===solution[j][0]-solution[j][1] /* same diagonal */ || solution[i][0]+solution[i][1]===solution[j][0]+solution[j][1] /* same anti-diagonal */ )
return false;
}
}
return true;
}

N = 4; // 4-Queens
// try filtering from all possible combinations of positions placed on NxN grid
o = Abacus.Combination(N*N, N, {output:toRowColumn}).filterBy(isValid);

echo(''+N+' Queens solutions (exhaustive search): START');
for(let solution of o) echo(solution);
echo(''+N+' Queens solutions (exhaustive search): END');

echo('---');

N = 3; // 3-Queens
// try filtering from all possible combinations of positions placed on NxN grid
o = Abacus.Combination(N*N, N, {output:toRowColumn}).filterBy(isValid);

echo(''+N+' Queens solutions (exhaustive search): START');
for(let solution of o) echo(solution);
echo(''+N+' Queens solutions (exhaustive search): END');

echo('---');

N = 4; // 4-Queens
// try reducing original search space by using only permutation of N!, each queen on different row
o = Abacus.Permutation(N, {output:toRowColumnP}).filterBy(isValid);

echo(''+N+' Queens solutions (reduced exhaustive search): START');
for(let solution of o) echo(solution);
echo(''+N+' Queens solutions (reduced exhaustive search): END');

echo('---');

// for some cases can construct N-queens solution from associated pan-diagonal magic/latin squares
N = 5; // 5-Queens
o = Abacus.LatinSquare.make(N);
echo(''+N+' Queens solution (pan-diagonal latin square): START');
o.forEach(function(oi){echo(oi.map(function(x){ return N===x ? 'X' : 'O'}).join(' '));});
echo(''+N+' Queen solutions (pan-diagonal latin square): END');
20 changes: 20 additions & 0 deletions examples/n_queens.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
4 Queens solutions (exhaustive search): START
[ [ 0, 1 ], [ 1, 3 ], [ 2, 0 ], [ 3, 2 ] ]
[ [ 0, 2 ], [ 1, 0 ], [ 2, 3 ], [ 3, 1 ] ]
4 Queens solutions (exhaustive search): END
---
3 Queens solutions (exhaustive search): START
3 Queens solutions (exhaustive search): END
---
4 Queens solutions (reduced exhaustive search): START
[ [ 0, 1 ], [ 1, 3 ], [ 2, 0 ], [ 3, 2 ] ]
[ [ 0, 2 ], [ 1, 0 ], [ 2, 3 ], [ 3, 1 ] ]
4 Queens solutions (reduced exhaustive search): END
---
5 Queens solution (pan-diagonal latin square): START
O O X O O
O O O O X
O X O O O
O O O X O
X O O O O
5 Queen solutions (pan-diagonal latin square): END
7 changes: 7 additions & 0 deletions examples/tsp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
var isNode = 'undefined' !== typeof global && '[object global]' === {}.toString.call(global);
var Abacus = isNode ? require('../src/js/Abacus.js') : window.Abacus, echo = console.log;

var o, N;
// solve exactly the TSP problem by Combinatorial methods using Abacus

// to do
74 changes: 69 additions & 5 deletions src/js/Abacus.js
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,23 @@ function product( x, i0, i1, ik )
{
return operate(Abacus.Arithmetic.mul, Abacus.Arithmetic.I, x, i0, i1, ik);
}
function gcd( /* args */ )
{
// https://en.wikipedia.org/wiki/Greatest_common_divisor
// https://en.wikipedia.org/wiki/Euclidean_algorithm
var args = arguments, c = args.length, a, b, t, i = 0, Arithmetic = Abacus.Arithmetic;
if ( 0 === c ) return Arithmetic.O;

a = Arithmetic.N(args[i++]);
while (i<c)
{
b = Arithmetic.N(args[i++]);
// swap them (a >= b)
if ( Arithmetic.lt(a,b) ) { t=b; b=a; a=t; }
while (!Arithmetic.equ(Arithmetic.O, b)) { t = b; b = Arithmetic.mod(a, t); a = t; }
}
return a;
}
function pow2( n )
{
var Arithmetic = Abacus.Arithmetic;
Expand Down Expand Up @@ -1199,6 +1216,7 @@ Abacus.Math = {

,sum: sum
,product: product
,gcd: gcd
,pow2: pow2
,exp: exp
,factorial: factorial
Expand Down Expand Up @@ -6696,12 +6714,58 @@ LatinSquare = Abacus.LatinSquare = Class({
,__static__: {
is_latin: is_latin
,make: function( n ) {
var i, j, k=1, s = new Array(n);
//s[0] = new Array(n); for (j=0; j<n; j++) s[0][j] = j+1;
for (i=0; i<n; i++)
// O(n x n)
var i, j, k=1, s = new Array(n), a, b, a2, b2, diag,
val = Abacus.Arithmetic.val, gcd = Abacus.Math.gcd;
// try to construct a (pan-)diagonal latin square first
if ( (n&1) /* odd */ && (n%3) /* not divisable by 3 */ )
{
a = 2; b = 1;
diag = 2; // conditions met for (pan-)diagonal square
}
else
{
s[i] = new Array(n);
for (j=0; j<n; j++) s[i][j] = (j+i)%n + 1;
// else try an exhaustive search over the possible factors
diag = 0;
for(i=1; i<n; i++)
{
if ( 1 === val(gcd(i, n)) ) a = i;
else continue;
for(j=i+1; j<n; j++)
{
if ( 1 === val(gcd(j, n)) ) b = j;
else continue;
a2 = a; b2 = b; // backup partial solution
diag = 1;
if ( 1 === val(gcd(a<b?b-a:a-b, n)) && 1 === val(gcd(a+b, n)) )
{
diag = 2; // conditions met for diagonal square
break;
}
}
}
if ( diag )
{
// get latest solutions
a = a2; b = b2;
}
}
if ( diag )
{
for (i=0; i<n; i++)
{
s[i] = new Array(n);
for (j=0; j<n; j++) s[i][j] = ((i*b)+(j*a))%n + 1;
}
}
else
{
// else default to a normal latin square
for (i=0; i<n; i++)
{
s[i] = new Array(n);
for (j=0; j<n; j++) s[i][j] = (j+i)%n + 1;
}
}
return s;
}
Expand Down
2 changes: 1 addition & 1 deletion src/js/Abacus.min.js

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions test/latin_squares.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,32 @@ true
true
---
1 2 3
2 3 1
3 1 2
2 3 1

true
---
1 2 3 4
2 3 4 1
3 4 1 2
4 1 2 3
3 4 1 2
2 3 4 1

true
---
1 2 3 4 5
2 3 4 5 1
3 4 5 1 2
4 5 1 2 3
5 1 2 3 4
1 3 5 2 4
2 4 1 3 5
3 5 2 4 1
4 1 3 5 2
5 2 4 1 3

true
---
1 2 3 4 5 6
2 3 4 5 6 1
3 4 5 6 1 2
4 5 6 1 2 3
5 6 1 2 3 4
6 1 2 3 4 5
5 6 1 2 3 4
4 5 6 1 2 3
3 4 5 6 1 2
2 3 4 5 6 1

true
---

0 comments on commit 33e83ff

Please sign in to comment.