Sudoku solver recursive backtracking not terminating - matlab

I wrote a MATLAB program that solves a 9 x 9 Sudoku puzzle using the recursive backtracking solution, but the recursion does not seem to terminate. When I pause the debugger and look at the board, I find that that my board already contains the correct solutions. In my approach, I work through the board elements column by column, starting from element 1 in (1, 1) and ending at element 81 at (9, 9). checkSudoku checks if the number is a valid placement by looking at the row, col and 3x3 subgrid. h is where the recursion occurs. Could anyone offer advice as to where my code went wrong?
function result = h(board, num)
if num >= 82
result = board;
else
if isnan(board(num))
flag = false;
c = ceil(num / 9);
r = num - ((c - 1) * 9);
n = 1;
while (n <= 9) & (~flag)
if checkSudoku(board, r, c, n)
board(num) = n;
product = h(board, num + 1);
if ~isnan(product)
flag = true;
board(num) = n;
else
board(num) = NaN;
n = n + 1;
end
else
n = n + 1;
end
end
if ~flag
result = NaN;
else
result = h(board, num + 1);
end
else
result = h(board, num + 1);
end
end
end
function safe = checkSudoku(board, row, col, num)
r = row;
c = col;
subrow = board(r, :);
subcol = board(:, col);
subBoard = zeros(3, 3);
if any([1 2 3] == r)
if any([1 2 3] == c)
subBoard = board(1:3, 1:3);
elseif any([4 5 6] == c)
subBoard = board(1:3, 4:6);
else
subBoard = board(1:3, 7:9);
end
elseif any([4 5 6] == r)
if any([1 2 3] == c)
subBoard = board(4:6, 1:3);
elseif any([4 5 6] == c)
subBoard = board(4:6, 4:6);
else
subBoard = board(4:6, 7:9);
end
else
if any([1 2 3] == c)
subBoard = board(7:9, 1:3);
elseif any([4 5 6] == c)
subBoard = board(7:9, 4:6);
else
subBoard = board(7:9, 7:9);
end
end
if any(subrow == num)
safe = false;
elseif any(subcol == num)
safe = false;
elseif any(any(subBoard == num))
safe = false;
else
safe = true;
end
end
function solvedBoard = solveSudoku(board)
solvedBoard = h(board, 1);
end
I took the problem and the MATLAB file from MITOpenCourseWare, homework 3 optional question 3. The file and photo can be found here.

Recursive functions can be tricky to abstract even in the simple cases. Your case have a extra layer of complexity as on top of having to calculate things based on previous iterations, the algorithm should also be able to backtrack a certain number of iterations, before continuing the way forward.
I made a working example, but it is not the only way to achieve the result. The way I propose make use of two flags to help the recursive function knows in which direction it is going. You could do without flags but it would involve doing more checks during the function to assess the state of the board. Since the capability is there to use flags I made use of it to simplify.
I would strongly recommend you read the documentation on return as it is a useful tool for these types of functions.
Now on to the answer:
The starting board:
Firstly, for everybody's benefit, I present the starting unsolved board. It is a 9x9 matrix containing the initial numbers and NaN everywhere else.
unsolvedBoard = [
5 3 NaN NaN 7 NaN NaN NaN NaN
6 NaN NaN 1 9 5 NaN NaN NaN
NaN 9 8 NaN NaN NaN NaN 6 NaN
8 NaN NaN NaN 6 NaN NaN NaN 3
4 NaN NaN 8 NaN 3 NaN NaN 1
7 NaN NaN NaN 2 NaN NaN NaN 6
NaN 6 NaN NaN NaN NaN 2 8 NaN
NaN NaN NaN 4 1 9 NaN NaN 5
NaN NaN NaN NaN 8 NaN NaN 7 9 ] ;
The starting conditions:
Your algorithm was iterating blindly over all the 99 possible boxes of the grid. The problem statement recommended you to identify the empty indices in the grid (to be placed in a emptyInd variable, and only iterate through these empty indices thanks to a variable ind.
To incorporate that I modified the start of the main solver:
function solvedBoard = solveSudoku(board)
emptyInd = find(isnan(board)) ; % find the empty indices in the grid
% this will solve the board recursively
solvedBoard = solverec( board, emptyInd, 1 );
end
Now emptyInd contains only 51 indices to be found. We'll only iterate on these and not on the 99 boxes of the grid.
The possible numbers for a given box:
Your function checkSudoku(board, row, col, num) was working perfectly fine, but can be simplified. You were already converting row and column indices to linear indices in your h function, you can reuse the same type of calculations in this function to know the indices of the subrow/subcol/subBoard.
Also note that you can merge the if conditions with the logical or to check all the conditions at once.
The function can become:
function safe = checkSudoku(board, row, col, num)
subrow = board(row, :);
subcol = board(:, col);
subSquareRow = (1:3) + 3*(ceil(row/3)-1) ;
subSquareCol = (1:3) + 3*(ceil(col/3)-1) ;
subBoard = board( subSquareRow , subSquareCol );
subBoard = subBoard(:) ; % Reshape into column vector (easier comparison)
% This whole block can be replaced with the line described below
if any(subrow == num) || any(subcol == num) || any(any(subBoard == num))
safe = false;
else
safe = true;
end
% Note that since we are dealing with boolean, the "IF" check above could
% be avoided and simply written as :
% safe = ~( any(subrow == num) || any(subcol == num) || any(any(subBoard == num)) ) ;
end
Now this function is later used in the recursive loop to check if a number from 1 to 9 is valid in a given position. You used a while loop to run from 1 to 9. I find that wasteful to check nine numbers when we could know from the start the few possible candidates for a given box. So I wrote a function which return a list of the only possible valid number for a box. If it returns only 3 possible numbers, I will only have to iterate through these 3 numbers, instead of doing it blindly over 9 ot them.
function candidates = getCandidates(board, row, col)
subrow = board(row, :);
subcol = board(:, col);
subSquareRow = (1:3) + 3*(ceil(row/3)-1) ;
subSquareCol = (1:3) + 3*(ceil(col/3)-1) ;
subBoard = board( subSquareRow , subSquareCol );
subBoard = subBoard(:) ; % Reshape into column vector (easier comparison)
% Get the difference of each array compared to a reference line
refval = 1:9 ;
cdrow = setdiff(refval,subrow) ;
cdcol = setdiff(refval,subcol) ;
cdsqr = setdiff(refval,subBoard) ;
% intersection of the three arrays
candidates = intersect( intersect(cdrow,cdcol) , cdsqr ) ;
end
You can read up on setdiff and on intersect to understand how it works.
Now the recursive solver:
This function is doing the job of your h() function. You were having 2 main problems in your implementation:
Too many conditional branches: the program flow had too many if
branches, and some paths were actually never used. Even when it works
it is confusing, but often confusion also introduce errors.
No robust condition to check when the board was fully solved: you had a check,
but it wasn't capturing the board completion (in part due to the problem above).
What was hapening is when your board was fully solved, the algorithm has no way to detect that and work back through the iterative function call with the final result in hand. You algorithm was finding the solution, but with the lack of an exit door for this case (fully solved), it was defaulting to the other branches and eventually consistently reverting the last few iterations, even though they were correct.
The following implementation seem to work ok for our test case and a couple of other. You can try it on other cases if you want, just be aware that the grid must be solvable. I did not put any check or instruction on what to do if the grid is not solvable so I don't know what will happen if you run it on such a grid.
Code for solverec.m:
function [res, solved, noSolutionFound] = solverec(board,emptyInd,ind,solved)
%% initialise the return flag for first function call
if nargin < 4 ; solved = false ; end
noSolutionFound = false ; % initialise second flag
% check if we are done with all the EmptyInd
if ind>numel(emptyInd) ;
solved = true ;
end
%% Return quickly if the board is already solved
if solved
res = board ;
return ;
end
%% If we are here, we still have to find new emptyInd
% prepare useful indices (row, column & linear index)
num = emptyInd(ind) ;
col = ceil(num / 9);
row = num - ((col - 1) * 9);
% get possible candidates for this box
cd = getCandidates(board, row, col) ;
ncd = numel(cd) ; % number of candidates
if ncd == 0
% no candidate for this box => back track
noSolutionFound = true ;
else
% Try the possible candidates one by one
for k=1:ncd ;
board(num) = cd(k) ; % try one candidate
% move on to next emptyInd
[res, solved, noSolutionFound] = solverec(board,emptyInd,ind+1,solved) ;
% bail out if solved
if solved ; return ; end
% otherwise, reset this emptyInd before trying next candidate
if noSolutionFound
board(num) = NaN ;
end
end
end
if noSolutionFound
% We have exhausted all possible candidates for this emptyInd
% We have to back track further
board(num) = NaN ;
res = board ;
return % this one is actually optional, the function will "return"
% anyway at the end of the "if" block.
end
end
Testing:
>> solvedBoard = solveSudoku(unsolvedBoard)
solvedBoard =
5 3 4 6 7 8 9 1 2
6 7 2 1 9 5 3 4 8
1 9 8 3 4 2 5 6 7
8 5 9 7 6 1 4 2 3
4 2 6 8 5 3 7 9 1
7 1 3 9 2 4 8 5 6
9 6 1 5 3 7 2 8 4
2 8 7 4 1 9 6 3 5
3 4 5 2 8 6 1 7 9
I'll let you write the optional displaySudoku(board) function as an exercise ;)

Related

Generate cell with random pairs without repetitions

How to generate a sequence of random pairs without repeating pairs?
The following code already generates the pairs, but does not avoid repetitions:
for k=1:8
Comb=[randi([-15,15]) ; randi([-15,15])];
T{1,k}=Comb;
end
When running I got:
T= [-3;10] [5;2] [1;-5] [10;9] [-4;-9] [-5;-9] [3;1] [-3;10]
The pair [-3,10] is repeated, which cannot happen.
PS : The entries can be positive or negative.
Is there any built in function for this? Any sugestion to solve this?
If you have the Statistics Toolbox, you can use randsample to sample 8 numbers from 1 to 31^2 (where 31 is the population size), without replacement, and then "unpack" each obtained number into the two components of a pair:
s = -15:15; % population
M = 8; % desired number of samples
N = numel(s); % population size
y = randsample(N^2, M); % sample without replacement
result = s([ceil(y/N) mod(y-1, N)+1]); % unpack pair and index into population
Example run:
result =
14 1
-5 7
13 -8
15 4
-6 -7
-6 15
2 3
9 6
You can use ind2sub:
n = 15;
m = 8;
[x y]=ind2sub([n n],randperm(n*n,m));
Two possibilities:
1.
M = nchoosek(1:15, 2);
T = datasample(M, 8, 'replace', false);
2.
T = zeros(8,2);
k = 1;
while (k <= 8)
t = randi(15, [1,2]);
b1 = (T(:,1) == t(1));
b2 = (T(:,2) == t(2));
if ~any(b1 & b2)
T(k,:) = t;
k = k + 1;
end
end
The first method is probably faster but takes up more memory and may not be practicable for very large numbers (ex: if instead of 15, the max was 50000), in which case you have to go with 2.

Convert for loop into vector expression

I'm new to MATLAB and learning to use vector expressions instead of verbose for loops. I have a snippet and I was wondering whether it could even be written in a concise vector and if so how would I modify it.
for v = I
X(i, v) = X(i, v) + length(I(I == v));
end
X is 1500x200
I is 3763x1
i can be thought of as a constant
What I'm doing here is this. I contains column indexes of X and I want to increment those locations by the number of times that particular index appeared in I. So after this for loop is done the ith row of X will contain a histogram.
Any other ideas or suggestions to improve my MATLAB coding would also be appreciated.
Here's a couple of ways:
I = randi(10, [50,1]);
X = zeros (1, 10);
for Col = 1 : size (X, 2)
X(1, Col) = sum (I == Col);
end
% X = 7 7 3 3 7 4 5 8 1 5
X = zeros (1, 10);
for Col = I.' % the transpose operation is needed to convert to horizontal!
X(1, Col) += 1;
end
% X = 7 7 3 3 7 4 5 8 1 5
X = zeros (1, 10);
X = accumarray (I, ones (size (I)), size (X))
% X = 7 7 3 3 7 4 5 8 1 5

Sort elements of rows in a matrix with another matrix

I have a matrix D of distances between 3 places and 4 persons
example D(2,3) = 10 means person 3 is far away from place 2 of 10 units.
D=[23 54 67 32
32 5 10 2
3 11 13 5]
another matrix A with the same number of rows (3 places) and where A(i,:) correspond to the persons that picked place i
example for place 1, persons 1 and 3 picked it
no one picked place 2
and persons 2 and 4 picked place 3
A=[1 3 0
0 0 0
2 4 0]
I want to reorder each row of A by the persons who are closest to the place it represents.
In this example, for place 1, person 1 is closer to it than person 3 based on D so nothing to do.
nothing to do for place 2
and there is a change for place 3 since person 4 is closer than 2 to place 3 D(3,2)>D(3,4)
The result should be
A=[1 3
0 0
4 2 ]
each row(place) in A can have 0 or many non zeros elements in it (persons that picked it)
Basically, I want to reorder elements in each row of A based on the rows of D (the closest to the location comes first), something like this but here A and D are not of the same size (number of columns).
[SortedD,Ind] = sort(D,2)
for r = 1:size(A,1)
A(r,:) = A(r,Ind(r,:));
end
There is another Matlab function sortrows(C,colummn_index) that can do the trick. It can sort rows based on the elements in a particular column. So if you transpose your matrix A (C = A') and extend the result by adding to the end the proper column, according to which you want to sort a required row, then you will get what you want.
To be more specific, you can do something like this:
clear all
D=[23 54 67 32;
32 5 10 2;
3 11 13 5];
A=[1 0;
3 0;
4 2 ];
% Sort elements in each row of the matrix A,
% because indices of elements in each row of the matrix D are always
% ascending.
A_sorted = sort(A,2);
% shifting all zeros in each row to the end
for i = 1:length(A_sorted(:,1))
num_zeros = sum(A_sorted(i,:)==0);
if num_zeros < length(A_sorted(i,:))
z = zeros(1,num_zeros);
A_sorted(i,:) = [A_sorted(i,num_zeros+1:length(A_sorted(i,:))) z];
end;
end;
% Prelocate in memory an associated array of the corresponding elements in
% D. The matrix Dr is just a reduced derivation from the matrix D.
Dr = zeros(length(A_sorted(:,1)),length(A_sorted(1,:)));
% Create a matrix Dr of elements in D corresponding to the matrix A_sorted.
for i = 1:length(A_sorted(:,1)) % i = 1:3
for j = 1:length(A_sorted(1,:)) % j = 1:2
if A_sorted(i,j) == 0
Dr(i,j) = 0;
else
Dr(i,j) = D(i,A_sorted(i,j));
end;
end;
end;
% We don't need the matrix A_sorted anymore
clear A_sorted
% In order to use the function SORTROWS, we need to transpose matrices
A = A';
Dr = Dr';
% The actual sorting procedure starts here.
for i = 1:length(A(1,:)) % i = 1:3
C = zeros(length(A(:,1)),2); % buffer matrix
C(:,1) = A(:,i);
C(:,2) = Dr(:,i);
C = sortrows(C,2);
A(:,i) = C(:,1);
% shifting all zeros in each column to the end
num_zeros = sum(A(:,i)==0);
if num_zeros < length(A(:,i))
z = zeros(1,num_zeros);
A(:,i) = [A(num_zeros+1:length(A(:,i)),i) z]';
end;
end;
% Transpose the matrix A back
A = A';
clear C Dr z

using a dynamic vector as an index value in for loop in Matlab

a=1:5
for k=a
if k<3
a=[a k+5];
end
disp(k)
end
When I run this code, I get these results:
1
2
3
4
5
k uses only the initial vector when it enters to the loop. I want it to update the values of a and take the new values of a too.
Thus, my question is how do I get this result:
1
2
3
4
5
6
7
ints = 1:5;
i = 0;
while i ~= length(ints)
i = i + 1;
if (i < 3)
ints = [ints i + 5]
end
disp(i)
end
a=1:5
i=1
while i<=length(a)
k=a(i);
if k<3
a=[a k+5];
end
disp(k)
i = i + 1
end
should do the trick. (Disclaimer: I didn't test it)
The index i iterates over a until it is really over.

Matlab - Not getting the expected result

I have written a function that is supposed to do the following:
Take as an input two sets
Take the distance between the two sets using pdist2 which code is shown here.
It will take the distance between the two sets at the beginning. Then, for the second set, at each iteration, it will set the (i,j) location to 0 and calculate the distance with this change. An, when it goes to the next iteration, it should change the next location value to '0' while at the same time return the preceding value which was set to '0' to its original value.
Note that the result from pdist2 originally return as a matrix, but for the comparison, I sum up the matrix values to use them for comparison.
Based on that, I have written the following function (note that you can use the pdist2.m function from the link here):
function m = pixel_minimize_distance(x,y)
sum1=0;
sum2=0;
[r c] = size(y);
d1 = pdist2(x,y);
[r1 c1] = size(d1);
for i=1:r1
for j=1:c1
sum1=sum1+d1(i,j);
end
end
maximum = sum1;
for i=1:r
for j=1:c
o = y(i,j)
y(i,j) = 0;
d2 = pdist2(x,y);
[r2 c2] = size(d2);
for i=1:r2
for j=1:c2
sum2=sum2+d2(i,j);
end
end
if sum2 >= maximum
if o ~= 0
maximum = sum2;
m = o;
end
end
if sum2 <= maximum
maximum = maximum;
end
y(i,j)=o;
end
end
end
Now, this is what I have run as a test:
>> A=[1 2 3; 6 5 4];
>> B=[4 5 3; 7 8 1];
>> pixel_minimize_distance(A,B)
o =
4
o =
4
o =
1
o =
7
o =
7
o =
0
ans =
7
See the the answer here is 7 (scroll down if you cannot see it), while the expected value when I calculate this manually should be 3, as since when we set it to 0 the sum of the distance will be 142.
Any idea what could be wrong in the code? I think it would be in the location in the code of setting o = y(i,j) where o denotes original value, but really couldn't figure a way of solving that.
Thanks.
I think you have many redundant commands in your code. I just removed them, nothing else. I am getting value of m as 3. I used MATLAB's pdist2 function with squared euclidean distance (since that is the default in the function you provided). I did not get 142 as the distance.
Here is the code:
function m = pixel_minimize_distance(x,y)
[r c] = size(y);
maximum = (sum(sum(pdist2(x,y)))).^2; %explained below
for i=1:r
for j=1:c
o = y(i,j);
y(i,j) = 0
sum2 = (sum(sum(pdist2(x,y)))).^2;
if sum2 >= maximum
if o ~= 0
maximum = sum2;
m = o;
end
end
y(i,j)=o;
end
end
end
and output is:
y =
0 5 3
7 8 1
y =
4 0 3
7 8 1
y =
4 5 0
7 8 1
y =
4 5 3
0 8 1
y =
4 5 3
7 0 1
y =
4 5 3
7 8 0
m =
3
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Explanation:
You have written the following snippet of code:
d2 = pdist2(x,y);
[r2 c2] = size(d2);
for i=1:r2
for j=1:c2
sum2=sum2+d2(i,j);
end
end
what this simply does is calculates the distance between two sets using pdist2 and sums up the entire distance matrix to come up with one value stored in sum2 in your case.
Lets look at the my code:
sum2 = (sum(sum(pdist2(x,y)))).^2;
pdist2 will give the distance. First sum command will sum along the rows and then the second one will sum along the columns to give you a total of all values in the matrix (This is what you did with two for loops). Now, the reason behind .^2 is:
In the original pdist2 function in the link which you have provided, you can see from the following snippet of code:
if( nargin<3 || isempty(metric) ); metric=0; end;
switch metric
case {0,'sqeuclidean'}
that squared Euclidean is the default distance, whereas in MATLAB, Euclidean distance is default. Therefore, I have squared the term.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%