1

I'm pretty new to coding in general and I'm writing a recursive sudoku solver right now in java. However, I keep getting a Stack Overflow error and I can't for the life of me figure out why.

Here's the whole code. The error supposedly lies in the various solve methods.

import java.util.*;
public class sudoku{

protected static int n;
protected static int[][] game;

public static boolean checkRow(int a, int b){
    boolean z = true;
    for(int i=0;i<n;i++){
        if(i==b) continue;
        else if(game[a][b]==game[a][i]){
            z = false;
            break;
        }
    }
    return(z);
}

public static boolean checkColumn(int a, int b){
    boolean z = true;
    for(int i=0;i<n;i++){
        if(i==a) continue;
        else if(game[i][b]==game[a][b]){
            z = false;
            break;
        }
    }
    return(z);
}

public static boolean checkBox(int a, int b){
    boolean z = true;
    int x = (int)Math.sqrt(n)*(int)(a/Math.sqrt(n));
    int y = (int)Math.sqrt(n)*(int)(b/Math.sqrt(n));
        for(int i=x;i<x+Math.sqrt(n);i++){
            for(int j=y;j<y+Math.sqrt(n);j++){
                if(a==i&&b==j) continue;
                else if(game[a][b]==game[i][j]){
                    z = false;
                    break;
                }
            }
        }
    return(z);
}

public static boolean checkAll(int a, int b){
    return(checkRow(a,b)&&checkColumn(a,b)&&checkBox(a,b));
}

public static void solvePrevious(int row, int col){
    if(row==0&&col==0){
        System.out.println("This game is unsolvable.");
        return;
    }
    else if(col==0) solve(row-1,n-1,game[row-1][n-1]+1);
    else solve(row,col-1,game[row][col]+1);
}

public static void solveNext(int row, int col){
    if(row==n-1&&col==n-1) return;
    else if(col==n-1) solve(row+1,0,1);
    else solve(row,col+1,1);
}

public static void solve(int row, int col, int value){
    if(value<=n){
        game[row][col] = value;
        if(checkAll(row,col)) solveNext(row,col);
        else solve(row,col,value+1);
    }
    else solvePrevious(row,col);
}

public static void main(String[] args){
    Scanner inp = new Scanner(System.in);
    System.out.println("What is the side length of the puzzle?");
    n = 0;
    do{
        n = inp.nextInt();
        if(Math.sqrt(n)%1!=0) System.out.println("The side length must be a perfect square.");
    }while(Math.sqrt(n)%1!=0);
    game = new int[n][n];
    solve(0,0,1);
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            System.out.print(game[i][j]+" ");
        }
        System.out.println(" ");
    }
}

}

NQ2Resq
  • 23
  • 1
  • 4
  • Your program is recursing too many times, consuming all of the available stack space. http://stackoverflow.com/questions/214741/what-is-a-stackoverflowerror – NAMS Apr 22 '16 at 21:58
  • Can you post the whole program? That would make it easier for us to run it and verify that our hints and tips are useful. – Roland Illig Apr 22 '16 at 21:59
  • I know what a Stack Overflow error is, but I can't figure out where. Is it just recursing too many times? Or is there an infinite recursion going on somewhere? @Roland Sure. Currently it's only for solving an empty sudoku board, but later I will change it to solve a board with preset cells as well. – NQ2Resq Apr 22 '16 at 22:03
  • It is 99% an endless recursion. To find it, try to do the opposite: try to verify that the recursion always stops. – Meier Apr 22 '16 at 22:47
  • @meier I feel like I'm missing something because i can't find any case where the recursion does not stop. It will always solve the cell using a higher value each time. It will always try to solve the next cell once the current cell is solved. If value in the cell is greater than n, meaning it could not be solved, it will always re-solve the previous cell with a higher value. If it tries to solve the cell before the first cell, it will end. If it solves the last cell successfully, it will end. What am I missing here? – NQ2Resq Apr 24 '16 at 01:07

1 Answers1

0

I think you don’t need to call solvePrevious from the solve method.

I’m a bit confused about the method names—the “previous” could either mean “previous cell” or “previous number for the same cell”. This is something you could improve.

The basic idea should look like this:

/* in main: */
solve(0, 0, 1);

solve(row, col, num) {
  if (checkAll(row, col)) {
    solveNextCell(row, col);
  }
  if (num < n * n) {
    solve(row, col, num + 1);
  }
}

solveNextCell(row, col) {
  if (row == n - 1 && col == n - 1)
    printSolution();
  else if (col == n - 1)
    solve(row + 1, 0, 1);
  else
    solve(row, col + 1, 1);
}

This way, you only “search forward”. Because the two methods call each other recursively, all the “previous” steps are remembered implicitly by the call hierarchy.

But even then will your program run a looooooong loooooong time, at least for the initially empty board. This is because it tries each number in each field of the first row, which are 9^9 = 387420489. Just assuming that in the second row there are similarly many positions, the number of tries grows to 150094635296999121, which is such a large number that I don’t know how to pronounce it. And that is just row 2 of 9.

So I suggest that you take an entirely different approach: As soon as you fill in a number, you mark that number as forbidden in the respective row, column and box. That way, you don’t have to loop over all the rows, columns and boxes each time. When I programmed a Sudoku solver, I had good success with remembering for each cell which numbers are still possible, and ruling them out as far as possible before even starting to recurse and trying every possible number.

Roland Illig
  • 40,703
  • 10
  • 88
  • 121