Skip to content

Commit

Permalink
v.0.9.4 contd
Browse files Browse the repository at this point in the history
* add .get() method to get all (filtered) results as an array, from current position to end
* add magic squares combinatorial example and update n-queens example
* update examples/readme
  • Loading branch information
foo123 committed May 22, 2019
1 parent b45e513 commit ba45be4
Show file tree
Hide file tree
Showing 7 changed files with 1,352 additions and 62 deletions.
193 changes: 164 additions & 29 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# 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.
The solutions, to popular problems, below exhibit the combinatorial capabilities, for solving complex combinatorial problems, of `Abacus` library. 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)
* [Magic Squares](#magic-squares)
* [Knapsack Problem](#knapsack)
* [TSP Problem](#tsp)

Expand All @@ -19,74 +20,208 @@ 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.
Let's assume we have some utility methods which allow us to check if a certain potential solution configuration is valid `is_valid` and also (we need that as well) a method (`row_column`) to map numeric patterns of combinatorial objects to `(row,column)` pairs on a hypothetical `NxN` grid. Also assume we have a utility method to make a `NxN` square grid with symbols `X` on `(row,col)` positions where a Queen is placed (`make_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.
With these utilities 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);
solutions = Abacus.Combination(N*N, N, {output:row_column}).filterBy(is_valid).get();

echo(''+N+' Queens solutions (exhaustive search): START');
for(let solution of o) echo(solution);
echo(''+N+' Queens solutions (exhaustive search): END');
echo(''+solutions.length+' Solutions for '+N+' Queens (exhaustive search):');
echo(solutions.map(function(solution){return print_grid(make_grid(solution));}).join("\n---\n"));
```

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
2 Solutions for 4 Queens (exhaustive search):
O X O O
O O O X
X O O O
O O X O
---
O O X O
X O O O
O O O X
O X O O
```

**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.
However searching among all combinations as above 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);
solutions = Abacus.Permutation(N, {output:row_column_perm}).filterBy(is_valid).get();

echo(''+N+' Queens solutions (reduced exhaustive search): START');
for(let solution of o) echo(solution);
echo(''+N+' Queens solutions (reduced exhaustive search): END');
echo(''+solutions.length+' Solutions for '+N+' Queens (reduced exhaustive search):');
echo(solutions.map(function(solution){return print_grid(make_grid(solution));}).join("\n---\n"));
```
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
2 Solutions for 4 Queens (reduced exhaustive search):
O X O O
O O O X
X O O O
O O X O
---
O O X O
X O O O
O O O X
O X O O
```

By the way let's **prove** there is **no solution for 3-Queens** using previous method. Piece of cake, simply set `N=3`!

```text
0 Solutions for 3 Queens (reduced exhaustive search):
```


**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).
If we only need to find one solution, any 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 the symbol/number `s` (whatever we choose that to be) from the available `N` different symbols/numbers.

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:
Since `Abacus` can generate `LatinSquare`s and also generates **pan-diagonal latin squares** by default 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');
solutions = [Abacus.LatinSquare.make(N)];

echo(''+solutions.length+' Solution for '+N+' Queens (pan-diagonal latin square):');
echo(solutions.map(function(solution){return print_grid(from_latin_square(solution));}).join("\n---\n"));
```

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

```text
5 Queens solution (pan-diagonal latin square): START
1 Solution for 5 Queens (pan-diagonal latin square):
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.
We saw how we can explore and solve the N-Queens problem with a two or three lines of code using `Abacus`.


### Magic Squares

[Magic Square, wikipedia](https://en.wikipedia.org/wiki/Magic_square)

see associated file: `examples/magic_squares.js`

Although `Abacus` can compute magic squares for all orders `N` (except `2`), still it only produces one magic square.
Let's try to find all possible magic squares (including rotated and reflected ones) systematically.


**Exhaustive Search**

First we can try an exhaustive search over all perumations of numbers `1..N^2` arranged in a square grid.

```javascript
solutions = Abacus.Permutation(N*N, {output:square}).filterBy(Abacus.MagicSquare.is_magic).get();

echo(''+solutions.length+' Solutions for '+N+'x'+N+' Magic Squares (exhaustive search):');
echo(solutions.map(print_square).join("\n---\n"));
```

The above for 3x3 magic squares gives:

```text
8 Solutions for 3x3 Magic Squares (exhaustive search):
2 7 6
9 5 1
4 3 8
---
2 9 4
7 5 3
6 1 8
---
4 3 8
9 5 1
2 7 6
---
4 9 2
3 5 7
8 1 6
---
6 1 8
7 5 3
2 9 4
---
6 7 2
1 5 9
8 3 4
---
8 1 6
3 5 7
4 9 2
---
8 3 4
1 5 9
6 7 2
```

By the way, let's **prove** that there are no `2x2` magic squares (under standard definition). It's super easy and fast.
Setting `N=2` we get **zero solutions**:

```text
0 Solutions for 2x2 Magic Squares (exhaustive search):
```

**Reduced Search**

Exhaustive search is very inefficient as `N` grows (even for small N such as 4 or 5 we get an enormous search space).
We can try something else. We can generate a magic square (for example using `Abacus.MagicSquare.make(N)`) and try to permute its rows and columns and see if we get different magic squares. This is considerably faster, but might not generate all possible magic squares of order `N`.

Let's try this:

```javascript
square = Abacus.MagicSquare.make(N);
solutions = square ? Abacus.Permutation(N).concatWith(Abacus.Permutation(N)).get() : [];

solutions = solutions.reduce(function(solutions, solution){
var permuted = permute_rows_cols(square, solution.slice(0,N), solution.slice(N));
if ( Abacus.MagicSquare.is_magic(permuted) ) solutions.push(permuted);
return solutions;
}, []);

echo(''+solutions.length+' Solutions for '+N+'x'+N+' Magic Squares (limited search):');
echo(solutions.map(print_square).join("\n---\n"));
```

Setting `N=4` we get:

```text
192 Solutions for 4x4 Magic Squares (limited search):
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
---
16 2 3 13
9 7 6 12
5 11 10 8
4 14 15 1
---
5 11 10 8
16 2 3 13
4 14 15 1
9 7 6 12
---
5 11 10 8
4 14 15 1
16 2 3 13
9 7 6 12
---
..etc..
```

Setting `N=6` we get 1440 solutions with our limited search.

We saw how we can investigate properties of magic squares and also enumerate them efficiently using a few lines of code with `Abacus`.


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

var solutions, N, square;
// compute all magic squares of order N by Combinatorial methods using Abacus

// utility functions
function square(item)
{
// arrange a permutation of 0..N^2-1 to NxN grid of symbols 1..N^2
var N = Math.floor(Math.sqrt(item.length)),
i, j, k,
output = new Array(N);
for(i=0,k=0; i<N; i++)
{
output[i] = new Array(N);
for(j=0; j<N; j++,k++) output[i][j] = item[k] + 1;
}
return output;
}
function format_num( n, l )
{
var s = String(n), sl = s.length;
return sl < l ? new Array(l-sl+1).join(' ')+s : s;
}
function print_square(grid)
{
for(var out='',i=0,N=grid.length,LEN = String(N*N).length; i<N; i++)
out += grid[i].map(function(x){ return format_num(x, LEN); }).join(' ') + (i+1<N?"\n":"");
return out;
}
function permute_rows_cols(square, row_perm, col_perm)
{
var N = square.length, i, j, output = new Array(N);
for(i=0; i<N; i++)
{
output[i] = new Array(N);
for(j=0; j<N; j++)
output[i][j] = square[row_perm[i]][col_perm[j]];
}
return output;
}

N = 3; // 3-Magic
// try filtering from all possible permutations of 1..N^2
solutions = Abacus.Permutation(N*N, {output:square}).filterBy(Abacus.MagicSquare.is_magic).get();

echo(''+solutions.length+' Solutions for '+N+'x'+N+' Magic Squares (exhaustive search):');
echo(solutions.map(print_square).join("\n---\n"));

echo('---');

N = 2; // 2-Magic
// try filtering from all possible permutations of 1..N^2
solutions = Abacus.Permutation(N*N, {output:square}).filterBy(Abacus.MagicSquare.is_magic).get();

echo(''+solutions.length+' Solutions for '+N+'x'+N+' Magic Squares (exhaustive search):');
echo(solutions.map(print_square).join("\n---\n"));

echo('---');

N = 4; // 4-Magic
square = Abacus.MagicSquare.make(N);
solutions = square ? Abacus.Permutation(N).concatWith(Abacus.Permutation(N)).get() : [];

solutions = solutions.reduce(function(solutions, solution){
var permuted = permute_rows_cols(square, solution.slice(0,N), solution.slice(N));
if ( Abacus.MagicSquare.is_magic(permuted) ) solutions.push(permuted);
return solutions;
}, []);

echo(''+solutions.length+' Solutions for '+N+'x'+N+' Magic Squares (limited search):');
echo(solutions.map(print_square).join("\n---\n"));

echo('---');

N = 6; // 6-Magic
square = Abacus.MagicSquare.make(N);
solutions = square ? Abacus.Permutation(N).concatWith(Abacus.Permutation(N)).get() : [];

solutions = solutions.reduce(function(solutions, solution){
var permuted = permute_rows_cols(square, solution.slice(0,N), solution.slice(N));
if ( Abacus.MagicSquare.is_magic(permuted) ) solutions.push(permuted);
return solutions;
}, []);

echo(''+solutions.length+' Solutions for '+N+'x'+N+' Magic Squares (limited search):');
//echo(solutions.map(print_square).join("\n---\n"));

echo('---');
Loading

0 comments on commit ba45be4

Please sign in to comment.