0

I wrote a backtracking algorithm to generate sudokus with arbitrary size. My problem is the varying speed of this generation algorithm.

I added some console output to see the results. I generated ~20 Sudokus with the size of 16x16, each one had 1ms to 150ms generating time. The next one took over 10 minutes, and it didn't even finish. (I stopped the program). In consideration of that, I think I made a mistake in the backtracking implementation, which prevents the algorithm from advancing.

Here is the code:

public class Generator {
    public static NormalSudoku generateNormal(int size) {
    NormalSudoku ns = new NormalSudoku(size);
    boolean[][][] track = new boolean[size][size][size];
    ArrayList<Integer> vals = new ArrayList<Integer>();
    fill(vals, size);

    int row = 0;
    int col = 0;
    int val = 0;

    int count = 0;
    while (row < size) {
        boolean found = false;
        while (!vals.isEmpty() && !found) {
            val = vals.get((int) (Math.random() * vals.size()));
            track[row][col][val - 1] = true;
            if (ns.checkValue(row, col, val)) {
                 ns.setValue(row, col, val);
                 fill(vals, size);
                 col++;
                found = true;
            } else {
                 vals.remove(Integer.valueOf(val));
            }

            if (col >= size) {
                 row++;
                 col = 0;
            }
        }
        if (vals.isEmpty()) {
        ns.setValue(row, col, 0);
        for (int i = 0; i < size; i++) {
            track[row][col][i] = false;
        }
        col--;
        if (col < 0) {
            row--;
            col = size - 1;
        }
        ns.setValue(row, col, 0);
        fill(vals, track, row, col);
        }
    }
    return ns;
    }

    private static void fill(ArrayList<Integer> vals, int size) {
    vals.clear();
    for (int i = 1; i <= size; i++) {
        vals.add(i);
    }
    }

    private static void fill(ArrayList<Integer> vals, boolean[][][] track,
        int row, int col) {
    vals.clear();
    for (int i = 0; i < track[row][col].length; i++) {
        if (!track[row][col][i]) {
        vals.add(Integer.valueOf(i + 1));
        }
    }
    }
}

Explanation of the code:

boolean[][][] track:

some sort of checklist for the values which are already tried (to reduce overhead of trying the same random value multiple times). I put track[row][col][val] on true if I already tried to put val on this field. If I go back, I have to clear the marks for that cell, to make the values possible again.

ns.checkValue:

Sets the value on the cell, checks if the sudoku is still correct, sets the old value again and returns true/false.

ns.setValue:

Simply sets the value on the cell.

vals:

An array list containing the values which are not tried yet for that cell. If I move forward, the list obviously must contain every possible number. If I backtrack, the list only has to contain the numbers which aren't tried yet. (I use the track array to realize that).

What am I doing wrong?

Greets

leyren
  • 524
  • 6
  • 20
  • 1
    You might want to post that to [codereview](http://codereview.stackexchange.com/). –  Nov 05 '15 at 18:47
  • i also tried making a prefilled sudoku program in my early programming days, first you need to analyze all the possible rules for a completely filled sudoku, you will find out that backtracking alone won't suffice, and that is why sometimes the program never finishes, i faced the same situations too... – Sarthak Mittal Nov 05 '15 at 19:57

1 Answers1

1

Ok first thing for some reason you chose a random number from vals list which most of the time is full with 10+ numbers, so you lose quite alot of time there. I tested your code and it really is quite random about the duration of the solving. For instance on this temporary solving

[12, 9, 14, 7, 5, 13, 8, 2, 15, 3, 4, 6, 16, 1, 10, 11]
[1, 2, 13, 15, 12, 16, 14, 4, 11, 8, 10, 9, 5, 6, 0, 0]
....

It takes too much time to fill cells (2,15) and (2,16) becouse it chooses a random from list vals with 10 values available for some reason but the possible values are numbers 3 and 7.

In the vals list you should exclude numbers from the current row and current column and region.

However this does not improve much the algorithm.

Your algorith is:

1. For current cell try all values until you find one that fits.
2. If it fits track it and move on to next cell.
3. If no value fits take step back, try value on the previous cell that was not used yet.

Becouse of the randomness your taking too much time on point 3 if some value is not on good place in some very first positions.

Zpetkov
  • 175
  • 1
  • 8
  • Well, since I want to generate (not solve) them, I need some randomness, otherwise I would always generate the same one. Thank you for the thing with the vals list, I didn't saw that. – leyren Nov 06 '15 at 06:01
  • My next suggestion is you have autoboxing on vals.add(i); in the fill method. Also you are adding Integers to the list with for cycle which is kinda slow because you can store the Integers [1, size] in a list variable allValues, when you fill you do vals.clear() vals.addAll(allValues). addAll is much faster than for cycle adding. – Zpetkov Nov 06 '15 at 06:49