开发者

Brute force Algorithm for creation of Sudoku Board

开发者 https://www.devze.com 2023-01-14 10:51 出处:网络
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).

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 开发者_JS百科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.


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();
        }
    }
}


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).


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消