September 2017 Update

This commit is contained in:
Ingy döt Net 2017-09-23 10:01:46 +02:00
parent bba7bfd280
commit ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions

View file

@ -0,0 +1,273 @@
//-------------------------------------------[ Dancing Links and Algorithm X ]--
/**
* The doubly-doubly circularly linked data object.
* Data object X
*/
class DoX {
/**
* @param {string} V
* @param {!DoX=} H
*/
constructor(V, H) {
this.V = V;
this.L = this;
this.R = this;
this.U = this;
this.D = this;
this.S = 1;
this.H = H || this;
H && (H.S += 1);
}
}
/**
* Helper function to help build a horizontal doubly linked list.
* @param {!DoX} e An existing node in the list.
* @param {!DoX} n A new node to add to the right of the existing node.
* @return {!DoX}
*/
const addRight = (e, n) => {
n.R = e.R;
n.L = e;
e.R.L = n;
return e.R = n;
};
/**
* Helper function to help build a vertical doubly linked list.
* @param {!DoX} e An existing node in the list.
* @param {!DoX} n A new node to add below the existing node.
*/
const addBelow = (e, n) => {
n.D = e.D;
n.U = e;
e.D.U = n;
return e.D = n;
};
/**
* Verbatim copy of DK's search algorithm. The meat of the DLX algorithm.
* @param {!DoX} h The root node.
* @param {!Array<!DoX>} s The solution array.
*/
const search = function(h, s) {
if (h.R == h) {
printSol(s);
} else {
let c = chooseColumn(h);
cover(c);
for (let r = c.D; r != c; r = r.D) {
s.push(r);
for (let j = r.R; r !=j; j = j.R) {
cover(j.H);
}
search(h, s);
r = s.pop();
for (let j = r.R; j != r; j = j.R) {
uncover(j.H);
}
}
uncover(c);
}
};
/**
* Verbatim copy of DK's algorithm for choosing the next column object.
* @param {!DoX} h
* @return {!DoX}
*/
const chooseColumn = h => {
let s = Number.POSITIVE_INFINITY;
let c = h;
for(let j = h.R; j != h; j = j.R) {
if (j.S < s) {
c = j;
s = j.S;
}
}
return c;
};
/**
* Verbatim copy of DK's cover algorithm
* @param {!DoX} c
*/
const cover = c => {
c.L.R = c.R;
c.R.L = c.L;
for (let i = c.D; i != c; i = i.D) {
for (let j = i.R; j != i; j = j.R) {
j.U.D = j.D;
j.D.U = j.U;
j.H.S = j.H.S - 1;
}
}
};
/**
* Verbatim copy of DK's cover algorithm
* @param {!DoX} c
*/
const uncover = c => {
for (let i = c.U; i != c; i = i.U) {
for (let j = i.L; i != j; j = j.L) {
j.H.S = j.H.S + 1;
j.U.D = j;
j.D.U = j;
}
}
c.L.R = c;
c.R.L = c;
};
//-----------------------------------------------------------[ Print Helpers ]--
/**
* Given the standard string format of a grid, print a formatted view of it.
* @param {!string|!Array} a
*/
const printGrid = function(a) {
const getChar = c => {
let r = Number(c);
if (isNaN(r)) { return c }
let o = 48;
if (r > 9 && r < 36) { o = 55 }
if (r >= 36) { o = 61 }
return String.fromCharCode(r + o)
};
a = 'string' == typeof a ? a.split('') : a;
let U = Math.sqrt(a.length);
let N = Math.sqrt(U);
let line = new Array(N).fill('+').reduce((p, c) => {
p.push(... Array.from(new Array(1 + N*2).fill('-')));
p.push(c);
return p;
}, ['\n+']).join('') + '\n';
a = a.reduce(function(p, c, i) {
let d = i && !(i % U), G = i && !(i % N);
i = !(i % (U * N));
d && !i && (p += '|\n| ');
d && i && (p += '|');
i && (p = '' + p + line + '| ');
return '' + p + (G && !d ? '| ' : '') + getChar(c) + ' ';
}, '') + '|' + line;
console.log(a);
};
/**
* Given a search solution, print the resultant grid.
* @param {!Array<!DoX>} a An array of data objects
*/
const printSol = a => {
printGrid(a.reduce((p, c) => {
let [i, v] = c.V.split(':');
p[i * 1] = v;
return p;
}, new Array(a.length).fill('.')));
};
//----------------------------------------------[ Grid to Exact cover Matrix ]--
/**
* Helper to get some meta about the grid.
* @param {!string} s The standard string representation of a grid.
* @return {!Array}
*/
const gridMeta = s => {
const g = s.split('');
const cellCount = g.length;
const tokenCount = Math.sqrt(cellCount);
const N = Math.sqrt(tokenCount);
const g2D = g.map(e => isNaN(e * 1) ?
new Array(tokenCount).fill(1).map((_, i) => i + 1) :
[e * 1]);
return [cellCount, N, tokenCount, g2D];
};
/**
* Given a cell grid index, return the row, column and box indexes.
* @param {!number} n The n-value of the grid. 3 for a 9x9 sudoku.
* @return {!function(!number): !Array<!number>}
*/
const indexesN = n => i => {
let c = Math.floor(i / (n * n));
i %= n * n;
return [c, i, Math.floor(c / n) * n + Math.floor(i / n)];
};
/**
* Given a puzzle string, reduce it to an exact-cover matrix and use
* Donald Knuth's DLX algorithm to solve it.
* @param puzString
*/
const reduceGrid = puzString => {
printGrid(puzString);
const [
numCells, // The total number of cells in a grid (81 for a 9x9 grid)
N, // the 'n' value of the grid. (3 for a 9x9 grid)
U, // The total number of unique tokens to be placed.
g2D // A 2D array representation of the grid, with each element
// being an array of candidates for a cell. Known cells are
// single element arrays.
] = gridMeta(puzString);
const getIndex = indexesN(N);
/**
* The DLX Header row.
* Its length is 4 times the grid's size. This is to be able to encode
* each of the 4 Sudoku constrains, onto each of the cells of the grid.
* The array is initialised with unlinked DoX nodes, but in the next step
* those nodes are all linked.
* @type {!Array.<!DoX>}
*/
const headRow = new Array(4 * numCells)
.fill('')
.map((_, i) => new DoX(`H${i}`));
/**
* The header row root object. This is circularly linked to be to the left
* of the first header object in the header row array.
* It is used as the entry point into the DLX algorithm.
* @type {!DoX}
*/
let H = new DoX('ROOT');
headRow.reduce((p, c) => addRight(p, c), H);
/**
* Transposed the sudoku puzzle into a exact cover matrix, so it can be passed
* to the DLX algorithm to solve.
*/
for (let i = 0; i < numCells; i++) {
const [ri, ci, bi] = getIndex(i);
g2D[i].forEach(num => {
let id = `${i}:${num}`;
let candIdx = num - 1;
// The 4 columns that we will populate.
const A = headRow[i];
const B = headRow[numCells + candIdx + (ri * U)];
const C = headRow[(numCells * 2) + candIdx + (ci * U)];
const D = headRow[(numCells * 3) + candIdx + (bi * U)];
// The Row-Column Constraint
let rcc = addBelow(A.U, new DoX(id, A));
// The Row-Number Constraint
let rnc = addBelow(B.U, addRight(rcc, new DoX(id, B)));
// The Column-Number Constraint
let cnc = addBelow(C.U, addRight(rnc, new DoX(id, C)));
// The Block-Number Constraint
addBelow(D.U, addRight(cnc, new DoX(id, D)));
});
}
search(H, []);
};

View file

@ -0,0 +1,25 @@
[
'819..5.....2...75..371.4.6.4..59.1..7..3.8..2..3.62..7.5.7.921..64...9.....2..438',
'53..247....2...8..1..7.39.2..8.72.49.2.98..7.79.....8.....3.5.696..1.3...5.69..1.',
'..3.2.6..9..3.5..1..18.64....81.29..7.......8..67.82....26.95..8..2.3..9..5.1.3..',
'394..267....3..4..5..69..2..45...9..6.......7..7...58..1..67..8..9..8....264..735',
'97.3...6..6.75.........8.5.......67.....3.....539..2..7...25.....2.1...8.4...73..',
'4......6.5...8.9..3....1....2.7....1.9.....4.8....3.5....2....7..6.5...8.1......6',
'85...24..72......9..4.........1.7..23.5...9...4...........8..7..17..........36.4.',
'..1..5.7.92.6.......8...6...9..2.4.1.........3.4.8..9...7...3.......7.69.1.8..7..',
'.9...4..7.....79..8........4.58.....3.......2.....97.6........4..35.....2..6...8.',
'12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8',
'9..2..5...4..6..3...3.....6...9..2......5..8...7..4..37.....1...5..2..4...1..6..9',
'1....7.9..3..2...8..96..5....53..9...1..8...26....4...3......1..4......7..7...3..',
'12.4..3..3...1..5...6...1..7...9.....4.6.3.....3..2...5...8.7....7.....5.......98',
'..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9',
'.......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7.....',
'....839..1......3...4....7..42.3....6.......4....7..1..2........8...92.....25...6',
'..3......4...8..36..8...1...4..6..73...9..........2..5..4.7..686........7..6..5..'
].forEach(reduceGrid);
// Or of you want to create all the grids of a particular n-size.
// I run out of stack space at n = 9
let n = 2;
let s = new Array(Math.pow(n, 4)).fill('.').join('');
reduceGrid(s);

View file

@ -0,0 +1,67 @@
function check(i, j)
id, im = div(i, 9), mod(i, 9)
jd, jm = div(j, 9), mod(j, 9)
jd == id && return true
jm == im && return true
div(id, 3) == div(jd, 3) &&
div(jm, 3) == div(im, 3)
end
const lookup = zeros(Bool, 81, 81)
for i in 1:81
for j in 1:81
lookup[i,j] = check(i-1, j-1)
end
end
function solve_sudoku(callback::Function, grid::Array{Int64})
(function solve()
for i in 1:81
if grid[i] == 0
t = Dict{Int64, Void}()
for j in 1:81
if lookup[i,j]
t[grid[j]] = nothing
end
end
for k in 1:9
if !haskey(t, k)
grid[i] = k
solve()
end
end
grid[i] = 0
return
end
end
callback(grid)
end)()
end
function display(grid)
for i in 1:length(grid)
print(grid[i], " ")
i % 3 == 0 && print(" ")
i % 9 == 0 && print("\n")
i % 27 == 0 && print("\n")
end
end
grid = Int64[5, 3, 0, 0, 2, 4, 7, 0, 0,
0, 0, 2, 0, 0, 0, 8, 0, 0,
1, 0, 0, 7, 0, 3, 9, 0, 2,
0, 0, 8, 0, 7, 2, 0, 4, 9,
0, 2, 0, 9, 8, 0, 0, 7, 0,
7, 9, 0, 0, 0, 0, 0, 8, 0,
0, 0, 0, 0, 3, 0, 5, 0, 6,
9, 6, 0, 0, 1, 0, 3, 0, 0,
0, 5, 0, 6, 9, 0, 0, 1, 0]
solve_sudoku(display, grid)

View file

@ -0,0 +1,173 @@
Init_board=[...
5 3 0 0 7 0 0 0 0;...
6 0 0 1 9 5 0 0 0;...
0 9 8 0 0 0 0 6 0;...
8 0 0 0 6 0 0 0 3;...
4 0 0 8 0 3 0 0 1;...
7 0 0 0 2 0 0 0 6;...
0 6 0 0 0 0 2 8 0;...
0 0 0 4 1 9 0 0 5;...
0 0 0 0 8 0 0 7 9];
break_point=1.0d5; //if 0 there will be no break
function []=disp_board(board)
StringBoard=string(board);
for i=1:9
for j=1:9
if board(i,j)==0 then
StringBoard(i,j)='×';
end
end
end
StringBoard=[StringBoard, string(zeros(9,2))];
StringBoard=[StringBoard; string(zeros(2,11))];
for i=1:9
StringBoard(i,:)=[StringBoard(i,1:3), '|', StringBoard(i,4:6), '|', StringBoard(i,7:9)]
end
StringBoard(9:11,:)=StringBoard(7:9,:);
StringBoard(8,:)=strsplit('-----------');
StringBoard(5:7,:)=StringBoard(4:6,:);
StringBoard(4,:)=strsplit('-----------');
disp(StringBoard)
endfunction
function varargout=validate_input(input,position,board)
row=board(position(1),:);
column=board(:,position(2));
block=zeros(3,3);
if position(1)>=1 & position(1)<=3 then
i=0;
elseif position(1)>=4 & position(1)<=6 then
i=3;
else
i=6;
end
if position(2)>=1 & position(2)<=3 then
j=0;
elseif position(2)>=4 & position(2)<=6 then
j=3;
else
j=6;
end
block=board(i+1:i+3,j+1:j+3)
valid_input=%F;
valid_row=%F;
valid_col=%F;
valid_block=%F;
if find(input==row)==[] then
valid_row=%T;
end
if find(input==column)==[] then
valid_col=%T;
end
if find(input==block)==[] then
valid_block=%T;
end
if valid_row & valid_col & valid_block then
valid_input=%T;
end
varargout=list(valid_input,valid_row,valid_col,valid_block)
endfunction
function varargout=validate_board(board)
valid_flag1=%T;
for i=1:9
for j=1:9
if board(i,j)~= 0 then
check_board=Init_board;
check_board(i,j)=0;
valid_flag1=validate_input(board(i,j),[i j],check_board);
if ~valid_flag1 then
break
end
end
end
if ~valid_flag1 then
break
end
end
valid_flag2 = (length( find(board) ) >= 17); //enforces rule of minimum of 17 givens
//set it to always %T to ignore this rule
valid_board = (valid_flag1 & valid_flag2);
varargout=list(valid_board)
endfunction
disp('Initial board:');
disp_board(Init_board);
valid_init_board=validate_board(Init_board);
if ~valid_init_board then
error('Invalid initial board. Should follow sudoku rules and have at least 17 clues.');
end
blank=[];
for i=1:9
for j=1:9
if Init_board(i,j)== 0 then
blank=[blank; i j];
end
end
end
Solved_board=Init_board;
tic();
i=0; counter=0; breaked=%F;
while i<size(blank,'r')
i=i+1;
counter=counter+1;
pos=blank(i,:);
value=Solved_board(pos(1),pos(2));
valid_value=%F;
while valid_value==%F
value=value+1;
if value>=10
break
else
valid_value=validate_input(value,pos,Solved_board);
end
end
if valid_value & value<10 then
Solved_board(pos(1),pos(2))=value
else
Solved_board(pos(1),pos(2))=0;
i=i-2;
end
if counter==break_point
breaked=%T;
break
end
end
valid_solved_board=validate_board(Solved_board);
t2=toc();
if valid_solved_board & ~breaked then
disp('Solved!');
disp('Solution:');
disp_board(Solved_board);
disp('Time: '+string(t2)+'s.');
disp('Steps: '+string(counter)+'.');
elseif breaked
disp('Break point reached.');
disp('Time: '+string(t2)+'s.');
disp_board(Solved_board);
elseif ~valid_solved_board & ~breaked
disp('Invalid solution found.');
disp_board(Solved_board);
end

View file

@ -5,38 +5,34 @@ func check(i, j) is cached {
jd == id && return true
jm == im && return true
var id2 = id//3
var jd2 = jd//3
jd2 == id2 || return false
jm//3 == im//3
(id//3 == jd//3) &&
(jm//3 == im//3)
}
func solve(board) {
for i in ^board {
board[i] && next
var *t = board[^board -> grep {|j| check(i, j) }]
func solve(grid) {
for i in ^grid {
grid[i] && next
var t = [grid[{|j| check(i, j) }.grep(^grid)]].freq
{ |k|
t.contains(k) && next
board[i] = k
solve(board)
} * 9
t.has_key(k) && next
grid[i] = k
solve(grid)
} << 1..9
board[i] = 0
grid[i] = 0
return nil
}
for i in ^board {
print "#{board[i]} ";
for i in ^grid {
print "#{grid[i]} "
print " " if (3 -> divides(i+1))
print "\n" if (9 -> divides(i+1))
print "\n" if (27 -> divides(i+1))
}
}
var board = %i(
var grid = %i(
5 3 0 0 2 4 7 0 0
0 0 2 0 0 0 8 0 0
1 0 0 7 0 3 9 0 2
@ -50,4 +46,4 @@ var board = %i(
0 5 0 6 9 0 0 1 0
)
solve(board)
solve(grid)

View file

@ -111,7 +111,6 @@ let board = [
[4, 0, 0, 0, 0, 0, 0, 6, 0],
[5, 0, 0, 0, 8, 0, 9, 0, 0],
[3, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 2, 0, 7, 0, 0, 0, 0, 1],
[0, 9, 0, 0, 0, 0, 0, 4, 0],
[8, 0, 0, 0, 0, 3, 0, 5, 0],

View file

@ -0,0 +1,64 @@
func solving(board: [[Int]]) -> [[Int]] {
var board = board
var isSolved = false
while !isSolved {
for x in 0 ..< 9 {
for y in 0 ..< 9 {
if board[x][y] == 0 {
let known = Set(board.map { $0[y] } + board[x] + subgrid(board, pos: (x, y)))
let possible = Set(Array(1...9)).subtracting(known)
if possible.count == 1 {
board[x][y] = possible.first!
}
}
}
isSolved = 45 == board[x].reduce(0, +)
}
}
return board
}
func subgrid(_ board: [[Int]], pos: (Int, Int)) -> [Int] {
var r = [Int]()
var (x, y) = pos
x = x / 3 * 3
y = y / 3 * 3
for i in x ..< x + 3 {
for j in y ..< y + 3 {
r.append(board[i][j])
}
}
return r
}
func print(_ board: [[Int]]) {
for i in board.indices {
if i % 9 == 0 {
print(" -------------------")
}
for j in board.indices {
if j % board.count == 0 {
print("| ", terminator: "")
}
let digit = board[i][j]
print(digit != 0 ? digit : " ", terminator: "")
print(" ", terminator: "")
}
print("|")
}
print(" -------------------")
}
let puzzle = [
[0,2,0,4,5,0,7,0,9],
[0,0,0,1,0,9,0,3,0],
[0,0,8,0,0,0,1,0,4],
[0,4,0,0,6,1,0,7,0],
[5,0,6,0,3,0,0,1,0],
[0,3,0,0,0,2,0,9,0],
[3,0,4,0,7,5,0,6,8],
[0,9,0,0,1,0,3,0,7],
[0,0,2,0,0,3,0,0,1]
]
print(solving(board: puzzle))

View file

@ -0,0 +1,132 @@
/// @Author: Alexandre Felipe (o.alexandre.felipe@gmail.com)
/// @Date: 2017-May-10
///
//////////////////////////////////////////////////////////////////////////////
/// SudokuSolver ///
/// A class that fills up a sudoku board, the initial board is given ///
/// as an array preset_rows, the positions where preset_rows is zero ///
/// are to be determined. Three views of the sudoku board are created ///
/// and the uniqueness of its elements are defined by on constraint for ///
/// each view, one constraint ensures that the values are between 1 and ///
/// 9, and two other constraints are used to ensure that the values in ///
/// all three views agree to each other. ///
/// ///
/// ///
/// A solution using only the "rows" array would be possible, however ///
/// this illustrates better how one can relate different variables in ///
/// SystemVerilog Constrained randomization. ///
//////////////////////////////////////////////////////////////////////////////
class SudokuSolver;
rand int tiles[0:8][0:8];
rand int rows[0:8][0:8];
rand int cols[0:8][0:8];
int preset_rows[0:8][0:8];
constraint board_input {
foreach(preset_rows[i])foreach(preset_rows[i][j])
if(preset_rows[i][j] != 0) rows[i][j] == preset_rows[i][j];
}
constraint range {
foreach(rows[i]) foreach(rows[i][j])
rows[i][j] inside {[1:9]};
}
////////////////////////////////////////////////
/// Every number in a row is unique ///
////////////////////////////////////////////////
constraint rows_permutation {
foreach(rows[i]) foreach(rows[i][j1])
foreach(rows[i][j2])
if(j1 != j2) rows[i][j1] != rows[i][j2];
}
///////////////////////////////////////////////
/// Every number in a column is unique ///
///////////////////////////////////////////////
constraint cols_permutation {
foreach(cols[i]) foreach(cols[i][j1])
foreach(cols[i][j2])
if(j1 != j2) cols[i][j1] != cols[i][j2];
}
/////////////////////////////////////////////////
/// Every number in a tile (square) is unique ///
/////////////////////////////////////////////////
constraint tiles_permutation {
foreach(tiles[i]) foreach(tiles[i][j1])
foreach(tiles[i][j2])
if(j1 != j2) tiles[i][j1] != tiles[i][j2];
}
///////////////////////////////////////////////////
/// Makes sure that sure that the numbers in ///
/// each view agree with other views ///
///////////////////////////////////////////////////
constraint rows_vs_tiles {
foreach(tiles[i]) foreach(tiles[i][j])
tiles[i][j] == rows[(i/3) * 3 + (j/3)][3*(i%3)+(j%3)];
}
constraint rows_vs_cols {
foreach(cols[i]) foreach(cols[i][j])
cols[i][j] == rows[j][i];
}
///////////////////////////////////////////////////
/// Print the current state of the board in the ///
/// standard output ///
///////////////////////////////////////////////////
function void printBoard;
int i, j;
for(i = 0; i < 9; ++i) begin
if(i % 3 == 0)$display(" -------------");
$write(" ");
for(j = 0; j < 9; ++j) begin
if(j % 3 == 0) $write("|");
$write("%c", "0" + rows[i][j]);
end
$display("|");
end
$display(" -------------");
endfunction
function void printInitial;
int i, j;
for(i = 0; i < 9; ++i) begin
if(i % 3 == 0)$display("-------------");
for(j = 0; j < 9; ++j) begin
if(j % 3 == 0) $write("|");
if(preset_rows[i][j]) begin
$write("%c", "0" + preset_rows[i][j]);
end
else begin
$write(".");
end
end
$display("|");
end
$display("-------------");
endfunction
endclass
//////////////////////////////////////////////////////
/// Simple program instantiating the sudoku object ///
//////////////////////////////////////////////////////
program SudokuTest;
SudokuSolver board;
initial begin
board = new;
foreach(board.preset_rows[0][i]) board.preset_rows[i][i] = i+1;
$display("Initial Board:");
board.printInitial();
// Generate two different solutions for the board
if(board.randomize())begin
$display("One solution:");
board.printBoard();
end
else begin
$display("ERROR: Failed to generate first solution");
end
if(board.randomize())begin
$display("Another solution:");
board.printBoard();
end
else begin
$display("ERROR: Failed to generate second solution");
end
end
endprogram

View file

@ -0,0 +1,22 @@
fcn trycell(sdku,pos=0){
row,col:=pos/9, pos%9;
if(pos==81) return(True);
if(sdku[pos]) return(trycell(sdku, pos + 1));
used:=0;
foreach r in (9){ used=used.bitOr((1).shiftLeft(sdku[r*9 + col] - 1)) }
foreach c in (9){ used=used.bitOr((1).shiftLeft(sdku[row*9 + c] - 1)) }
row,col = row/3*3, col/3*3;
foreach r,c in ([row..row+2], [col..col+2])
{ used=used.bitOr((1).shiftLeft(sdku[r*9 + c] - 1)) }
sdku[pos]=1; while(sdku[pos]<=9){
if(used.isEven and trycell(sdku, pos + 1)) return(True);
sdku[pos]+=1; used/=2;
}
sdku[pos]=0;
return(False);
}

View file

@ -0,0 +1,19 @@
problem:=
#<<<
" 5 3 0 0 7 0 0 0 0
6 0 0 1 9 5 0 0 0
0 9 8 0 0 0 0 6 0
8 0 0 0 6 0 0 0 3
4 0 0 8 0 3 0 0 1
7 0 0 0 2 0 0 0 6
0 6 0 0 0 0 2 8 0
0 0 0 4 1 9 0 0 5
0 0 0 0 8 0 0 7 9";
#<<<
s:=problem.split().apply("toInt").copy(); // writable list of 81 ints
trycell(s).println();
println("+-----+-----+-----+");
foreach n in (3){
s[n*27,27].pump(Console.println,T(Void.Read,8),("| " + "%s%s%s | "*3).fmt); // 3 lines
println("+-----+-----+-----+");
}