5

What I am developing is that initially the entire sudoku board is empty. One of the random cells(out of 81) is filled with a random value(1-9).

Now I want to fill all the remaining cells using brute force approach.
From what I came to know after googling is that we should start with the first cell and fill it with 1(if it's valid), then fill the second cell with 2(if it's valid, we will begin checking with a number greater than the last filled cell, which in this case is 1, once we reach 9, we reset it with 1).

The thing is that it's not working properly!

Can anyone link me to the exact algorithm.

Akshay J
  • 5,362
  • 13
  • 68
  • 105

3 Answers3

3

Here's an implementation of the backtracking approach:

import java.util.Random;

public class Sudoku {

    public static void main(String[] args) {
        Random rand = new Random();
        int r = rand.nextInt(9);
        int c = rand.nextInt(9);
        int value = rand.nextInt(9) + 1;
        Board board = new Board();
        board.set(r, c, value);
        System.out.println(board);
        solve(board, 0);
        System.out.println(board);
    }

    private static boolean solve(Board board, int at) {
        if (at == 9*9)
            return true;
        int r = at / 9;
        int c = at % 9;
        if (board.isSet(r, c))
            return solve(board, at + 1);
        for (int value = 1; value <= 9; value++) {
            if (board.canSet(r, c, value)) {
                board.set(r, c, value);
                if (solve(board, at + 1))
                    return true;
                board.unSet(r, c);
            }
        }
        return false;
    }

    static class Board {
        private int[][] board = new int[9][9];
        private boolean[][] rs = new boolean[9][10];
        private boolean[][] cs = new boolean[9][10];
        private boolean[][][] bs = new boolean[3][3][10];
        public Board() {}
        public boolean canSet(int r, int c, int value) {
            return !isSet(r, c) && !rs[r][value] && !cs[c][value] && !bs[r/3][c/3][value];
        }
        public boolean isSet(int r, int c) {
            return board[r][c] != 0;
        }
        public void set(int r, int c, int value) {
            if (!canSet(r, c, value))
                throw new IllegalArgumentException();
            board[r][c] = value;
            rs[r][value] = cs[c][value] = bs[r/3][c/3][value] = true;
        }
        public void unSet(int r, int c) {
            if (isSet(r, c)) {
                int value = board[r][c];
                board[r][c] = 0;
                rs[r][value] = cs[c][value] = bs[r/3][c/3][value] = false;
            }
        }
        public String toString() {
            StringBuilder ret = new StringBuilder();
            for (int r = 0; r < 9; r++) {
                for (int c = 0; c < 9; c++)
                    ret.append(board[r][c]);
                ret.append("\n");
            }
            return ret.toString();
        }
    }
}
Sheldon L. Cooper
  • 3,230
  • 19
  • 13
0

I used a method without backtracing, although the while loop might be it. To quote an algorithm book I've read "Nothing in recursion can't be duplicated using iteration".

I've been using my eyes to inspect this, and since I can't wrap my head around the recursive method, even though recursion is relatively understood:

This method, I kinda wrote with some guidance, had a bug in the grid checker, when I found it, it seems to be working now. I'm positing it 'cause it's hard to find complete-and-working code. IOS SDK.

#define WIDTH 9
#define HEIGHT 9


@interface ViewController ()
//- (BOOL) numberConflicts:(int)testNum;
- (BOOL) number:(int)n conflictsWithRow:(int)r;
- (BOOL) number:(int)n conflictsWithColumn:(int)c;
- (BOOL) number:(int)n conflictsWithSquareInPointX:(int)x andPointY:(int)y;
- (BOOL) number:(int)n conflictsAtGridPointX:(int)xPoint andPointY:(int)yPoint;
- (int) incrementSudokuValue:(int)v;
@end


static int sudoku[WIDTH][HEIGHT];

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    /// Initialize it
    for (int x = 0; x < WIDTH; x++)
    {
        for (int y = 0; y < HEIGHT; y++)
        {
            sudoku[x][y] = 0;
        }
    }
    ///




    int tries = 0;
    for (int j = 0; j < HEIGHT; j++)
    {
        for (int i = 0; i < WIDTH; i++)
        {
            int num = arc4random()%9 + 1;
            while ([self number:num conflictsAtGridPointX:i andPointY:j])
            {
                num = [self incrementSudokuValue:num];
                tries++;
                if (tries > 10) { //restart the column
                    tries = 0;

                    for(int count = 0; count < WIDTH; count++)
                    {
                        sudoku[count][j] = 0;

                    }

                    i = 0;

                }
            }
            if(sudoku[i][j] == 0)
               sudoku[i][j] = num; 

            tries = 0;
            for (int y = 0; y < HEIGHT; y++)
            {
                for (int x = 0; x < WIDTH; x++)
                {
                    printf("%i ", sudoku[x][y]);
                }

                printf("\n");
            }

            printf("\n");

        }
    }

    for (int x = 0; x < WIDTH; x++)
    {
        for (int y = 0; y < HEIGHT; y++)
        {
            printf("%i ", sudoku[y][x]);
        }
        printf("\n"); //newline
    }

    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}



- (BOOL) number:(int)n conflictsWithRow:(int)r;
{
    for (int y = 0; y < HEIGHT; y++) {
        if (sudoku[y][r] == n) {
            return YES;
        }
    }

    return NO;
}

- (BOOL) number:(int)n conflictsWithColumn:(int)c;
{
    for (int x = 0; x < WIDTH; x++) {
        if (sudoku[c][x] == n) {
            return YES;
        }
    }

    return NO;
}

- (BOOL) number:(int)n conflictsAtGridPointX:(int)xPoint andPointY:(int)yPoint;
{
    if ([self number:n conflictsWithRow:yPoint])
    {
        return YES;
    }

    if ([self number:n conflictsWithColumn:xPoint])
    {
        return YES;
    }

    if ([self number:n conflictsWithSquareInPointX:xPoint andPointY:yPoint]) {
        return YES;
    }


    return NO;
}

- (BOOL) number:(int)n conflictsWithSquareInPointX:(int)x andPointY:(int)y;
{
    int leftX = x - (x % 3); //used to use int division
    // leftX *= 3;
    int topY = y - (y % 3);
    // topY *= 3;

    int rightX = leftX + 2;
    int bottomY = topY + 2;

    for(int subY = topY; subY <= bottomY; subY++) //bug was here, used < instead of less N equal to...
    {
        for ( int subX = leftX; subX <= rightX; subX++)
        {
            if (sudoku[subX][subY] == n) {
                return YES;
            }
        }
    }

    NSLog(@"Testing grid at %i, %i", x/3, y/3);
    NSLog(@"LeftX: %i TopY: %i", leftX, topY);

    return NO;
}

- (int) incrementSudokuValue:(int)v;
{
    if (v < 9) {
        v++;
        return v;
    }
    return 1;
}

Note: The header file is empty, paste this into iOS single View application if you desire.

Caution: might loop infinitely( and above does sometimes, but is very fast), may want another more global "tries" variable, and restart the algorithm as a safety, or give it a seed/do both

edit: the below should be safe from infinite loops, if the source grid is solvable (or nonexistant)


#define WIDTH 9
#define HEIGHT 9

@interface ViewController ()
//- (BOOL) numberConflicts:(int)testNum;
- (BOOL) number:(int)n conflictsWithRow:(int)r;
- (BOOL) number:(int)n conflictsWithColumn:(int)c;
- (BOOL) number:(int)n conflictsWithSquareInPointX:(int)x andPointY:(int)y;
- (BOOL) number:(int)n conflictsAtGridPointX:(int)xPoint andPointY:(int)yPoint;
- (int) incrementSudokuValue:(int)v;
@end

static int sudoku[WIDTH][HEIGHT];

@implementation ViewController

- (BOOL) fillGridWithNext:(int)next;
{

    for (int y = 0; y < HEIGHT; y++)
    {
        for (int x = 0; x < WIDTH; x++)
        {
            if (sudoku[x][y] != 0)
            {
                if (x == 8 && y == 8) {
                    return YES;
                }
                continue;
            }

            for (int count = 0; count < (HEIGHT-1); count++)
            {
                if ([self number:next conflictsAtGridPointX:x andPointY:y])
                {
                    next = [self incrementSudokuValue:next];
                }
                else
                {
                    sudoku[x][y] = next;
                    if( [self fillGridWithNext:arc4random()%9+1])
                    {
                        return YES;
                    }

                }
            }
            sudoku[x][y] = 0;
            return NO;
        }
    }

    return NO;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    /// Initialize it
    for (int x = 0; x < WIDTH; x++)
    {
        for (int y = 0; y < HEIGHT; y++)
        {
            sudoku[x][y] = 0;
        }
    }

    sudoku[0][0]=9;
    int next;
    next = (arc4random()%9)+1;

    if( [self fillGridWithNext:next]) //seeded
    {
        NSLog(@"Solved");
    }
    else
    {
        NSLog(@"No solution");
    }


    for (int x = 0; x < WIDTH; x++)
    {
        for (int y = 0; y < HEIGHT; y++)
        {
            printf("%i ", sudoku[y][x]);
        }
        printf("\n"); //newline
    }
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}



- (BOOL) number:(int)n conflictsWithRow:(int)r;
{
    for (int y = 0; y < HEIGHT; y++) {
        if (sudoku[y][r] == n) {
            return YES;
        }
    }

    return NO;
}

- (BOOL) number:(int)n conflictsWithColumn:(int)c;
{
    for (int x = 0; x < WIDTH; x++) {
        if (sudoku[c][x] == n) {
            return YES;
        }
    }

    return NO;
}

- (BOOL) number:(int)n conflictsAtGridPointX:(int)xPoint andPointY:(int)yPoint;
{
    if ([self number:n conflictsWithRow:yPoint])
    {
        return YES;
    }

    if ([self number:n conflictsWithColumn:xPoint])
    {
        return YES;
    }

    if ([self number:n conflictsWithSquareInPointX:xPoint andPointY:yPoint]) {
        return YES;
    }


    return NO;
}

- (BOOL) number:(int)n conflictsWithSquareInPointX:(int)x andPointY:(int)y;
{
    int leftX = x - (x % 3); //used to use int division
    // leftX *= 3;
    int topY = y - (y % 3);
    // topY *= 3;

    int rightX = leftX + 2;
    int bottomY = topY + 2;

    for(int subY = topY; subY <= bottomY; subY++) //bug was here, used < instead of less N equal to...
    {
        for ( int subX = leftX; subX <= rightX; subX++)
        {
            if (sudoku[subX][subY] == n) {
                return YES;
            }
        }
    }

    NSLog(@"Testing grid at %i, %i", x/3, y/3);
    NSLog(@"LeftX: %i TopY: %i", leftX, topY);

    return NO;
}

- (int) incrementSudokuValue:(int)v;
{
    if (v < 9) {
        v++;
        return v;
    }
    return 1;
}

@end

Summary: The first version is flawed but (mostly) gets the job done. It generates every row at random, if the row is invalid, it wipes and starts over. This will wipe out source grids, and can go forever, but works most of the time.

The lower code uses recursion. I don't think it backtracks properly, but it has solved empty and semi-seeded grids on my tests. I think I need to save a "state" grid to backtrack with, but I'm not doing this. I'm posting both since they both answer "Brute force"... on my own, I should study recursion, I can't explain why the lower one works, I personally could use help with doing it.

Note: The first one finishes in a blink or so when it does finish... if speed means more than reliability to your application (somewhat counter-intuitive in this case, with the infinite looping, heh).

Stephen J
  • 2,367
  • 2
  • 25
  • 31
  • To prevent the algorithm from being blocked, when it's on the 3rd row of any set of 3 rows, I'm guessing it should use the "other 2" grids' existing numbers as the available numbers. This prevents it from 'locking'. If this is correct, this algorithm becomes a perfectly-random generator. I think a solver is just a matter of seeding this grid. It's not optimized, though. Such a change would make the equation perfect, eliminating all possible "locks" for moving forward and needing to use the tries++ section. I can't prove this though, sry, not yet a math proof person – Stephen J Nov 21 '12 at 06:28
-2

This simple random walk algorithm should work too (but is inefficient- use at your own risk!!!):

EDIT: - added fix for unresolvable solutions.

For each empty cell in grid
    array = Get_Legal_Numbers_for_cell(row,col);
    If (array is empty) {
        Clear_All_cells()
    } else {
        number = Random_number_from(array);
        Put_Number_in_Cell(number);
    }

EDIT 2

If someone are interested here are described methods for solving sudoku with random-based search.

Agnius Vasiliauskas
  • 10,935
  • 5
  • 50
  • 70
  • No, it shouldn't work. There may be no legal numbers for a cell. Consider this example: 123456789 456123___ What are the legal numbers for the empty cells of the second row? – Sheldon L. Cooper Aug 30 '10 at 22:10
  • Now, it should work,- we just need to restart algorithm if there are no legal numbers for cell. Of course it is very inefficient. And yes - i know other efficient approaches like backtracking and such... – Agnius Vasiliauskas Aug 31 '10 at 07:21
  • That's not really a simple algorithm - for starters, it's random rather than brute force (and so might *never* find the right solution). Also when you clear all cells and restart, how does `Get_Legal_Numbers_for_cell` work out which value to change? Because *one* of the *n* values you've filled in might be incorrect, but how would you know which to change? By the time you've fleshed your methods out I think you'd find that's nowhere near simple. – Dan Puzey Aug 31 '10 at 07:28
  • @Dan Puzey Get_Legal_Numbers_for_cell doesn't calculates which value to change at all. It just gets ALL legal numbers for the current empty cell. If there are no such numbers - we clear all grid and restart algorithm. What's the problem with that random walk approach ? – Agnius Vasiliauskas Aug 31 '10 at 07:38
  • So you clear the grid and restart randomly - what if you re-run the same combination over and over? What if you never find the solution? I mean, there's brute-force "try every available combination", but your algorithm is actually less efficient than that! – Dan Puzey Aug 31 '10 at 08:58
  • About running the same combination over and over- according to probability theory joint probability that first row will be generated the same as was before is: 1/9*1/8*...*1/2*1/1 ~ 0.0000027 Given this - it is VERY unlikely that second algorithm restart will give the same dead-end solution over and over. About never finding solution - to research this question one should need to conduct experiment of this algorithm to find out the ratio dead-end-solutions/total_generation_tries. How did you measured that this algorithm is less efficient than brute-force ? – Agnius Vasiliauskas Aug 31 '10 at 11:34
  • See my EDIT 2 - I'm pretty sure - if there are methods for solving sudoku stochastically, then there should be similar stochastic methods for generating complete sudoku board. – Agnius Vasiliauskas Aug 31 '10 at 12:08
  • @Dan Puzey, this is sudoku not latin squares. – Sheldon L. Cooper Aug 31 '10 at 13:33
  • @Sheldon: ack, sorry - so it is :-) *facepalm* – Dan Puzey Aug 31 '10 at 14:08
  • FYI, I generated 10,000 sudokus with this method. For each generation, on average, it visited 10389.64 cells, with 319.66 restarts. My implementation fills cells from top to bottom, and left to right. – Sheldon L. Cooper Aug 31 '10 at 15:27
  • 1
    These numbers give an estimate of approx. 1 in 320 chance of finding a solution on each try. – Sheldon L. Cooper Aug 31 '10 at 15:34
  • I'm impressed, seems random walk is not so bad at all :) – Agnius Vasiliauskas Aug 31 '10 at 16:23