【问题标题】:Sudoku Brute Force Algorithm数独蛮力算法
【发布时间】:2015-01-10 14:51:51
【问题描述】:

我必须编写一个蛮力算法来用预先确定的方法解决数独问题。 至于解决方法,我有点精神障碍。作业说我们必须至少使用给定的方法,它们是isPermutationisPermutationRowisPermutationColisPermutationBlockisPermutationMatrixisValidsolve

我真的不知道什么时候在solve方法中返回值,因为它必须是递归的。

我会非常感谢任何帮助:)

package gdp.aufgabe22;

public class Sudoku {

public static void main(String[] args) {
    int[][] d = { {0,0,3,0,2,0,6,0,0},
                  {9,0,0,3,0,5,0,0,1},
                  {0,0,1,8,0,6,4,0,0},
                  {0,0,8,1,0,2,9,0,0},
                  {7,0,0,0,0,0,0,0,8},
                  {0,0,6,7,0,8,2,0,0},
                  {0,0,2,6,0,9,5,0,0},
                  {8,0,0,2,0,3,0,0,9},
                  {0,0,5,0,1,0,3,0,0}
    };
    int[][] s = solve(d);
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            System.out.print(s[i][j] + " ");
        }
        System.out.print("\n");
    }
    System.out.println();
}

public static boolean isPermutation(int[] a) {
    int[][] key = new int[2][a.length];
    key[0] = a;
    for (int i = 0; i < key.length; i++) {
        key[1][i] = 0;
    }
    for (int i = 0; i < key.length; i++) {
        if (a[i]>0) {
            key[1][a[i]-1]++;
        }
    }
    boolean keycheck = false;
    for (int i = 0; i < a.length; i++) {
        if(key[1][i]>1) {
            keycheck = true;
        }
    }
    if (keycheck == true) {
        return false;
    }
    else {
        return true;
    }
}

public static boolean isPermutationRow(int[][] a, int row) {
        int[] key = new int[a[row].length];
        key = a[row];
        return isPermutation(key);
}

public static boolean isPermutationCol(int[][] a, int col) {
    int[] key = new int[a.length];
    for (int i = 0; i < key.length; i++) {
        key[i] = a[i][col];
    }
    return isPermutation(key);
}

public static boolean isPermutationMatrix(int[][] a) {
    for (int i = 0; i < a.length; i++) {
        if (!isPermutationRow(a, i)) {
            return false;
        }
    }
    for (int i = 0; i < a.length; i++) {
        if (!isPermutationCol(a, i)) {
            return false;
        }
    }
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            switch (i) {
            case 0: switch(j) {
                case 0: if(!isPermutationBlock(a,0,2,0,2)) {
                    return false;
                }
                case 3: if(!isPermutationBlock(a,0,2,3,5)) {
                    return false;
                }
                case 6: if(!isPermutationBlock(a,0,2,6,8)) {
                    return false;
                }
                default: break;
            }
            case 3: switch(j) {
                case 0: if(!isPermutationBlock(a,3,5,0,2)) {
                    return false;
                }   
                case 3: if(!isPermutationBlock(a,3,5,3,5)) {
                    return false;
                }
                case 6: if(!isPermutationBlock(a,3,5,6,8)) {
                    return false;
                }
                default: break;
            }   
            case 6: switch(j) {
                case 0: if(!isPermutationBlock(a,6,8,0,2)) {
                    return false;
                }   
                case 3: if(!isPermutationBlock(a,6,8,3,5)) {
                    return false;
                }
                case 6: if(!isPermutationBlock(a,6,8,6,8)) {
                    return false;
                }
                default: break;
            }   
            default: break;
        }
        }
    }
    return true;
}

public static boolean isPermutationBlock(int[][] a, int minRow, int maxRow, int minCol, int maxCol) {
    int[][] key = new int[2][(maxRow-minRow+1)+(maxCol-minCol+1)];
    int[][] countfeld = new int[2][9];
    for (int i = 0; i < 9; i++) {
        countfeld[0][i] = i+1;
    }
    int keycount = 0;
    for (int i = minRow; i<maxRow; i++) {
        for (int j = minCol; j<maxCol; j++) {
            key[0][keycount] = a[i][j];
            keycount++;
        }
    }
    for (int i = 0; i < countfeld[0].length; i++) {
        countfeld[1][i] = 0;
    }
    for (int i = 0; i < key[0].length; i++) {
        if (key[0][i]>0) {
            countfeld[1][key[0][i]-1]++;
        }
    }
    boolean keycheck = false;
    for (int i = 0; i < key[0].length; i++) {
        if(countfeld[1][i]>1) {
            keycheck = true;
        }
    }
    if (keycheck == true) {
        return false;
    }
    else {
        return true;
    }
}

public static boolean isValid(int[][] a) {
    if (a.length != 9 || a[0].length != 9) {
        return false;
    }
    return (isPermutationMatrix(a));
}

public static int[][] solve(int[][] a) {
    int[] freeslot = findfreeslot(a);
    int f1 = freeslot[0];
    int f2 = freeslot[1];
    if (f1 == -1) {
        return a;
    }
    teilsolve(f1, f2, a);
    return a;
}

public static void teilsolve(int f1, int f2, int[][] a) {
    int[][] temp = new int[a.length][a[0].length];
    for (int y = 0; y < a.length; y++) {
        for (int z = 0; z < a[0].length; z++) {
            temp[y][z] = a[y][z];
        }
    }
    for (int i = 1; i < 10; i++) {
        a[f1][f2] = i;
        boolean valide = isValid(a);
        if (valide) {
            a = solve(a);
            break;
        }
    }
}

public static int[] findfreeslot(int[][]a) {
    int[] key = {-1,-1};
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            if (a[i][j] == 0) {
                key[0] = i;
                key[1] = j;
                return key;
            }
        }
    }
    return key;
}
}

【问题讨论】:

  • if (keycheck == true) { return false; } else { return true; }你的方法是这样给你的,我理解正确吗?
  • 方法头给了我,其他的都是我自己做的。我知道有些部分没有很好地编程,因为我是一个真正的菜鸟:D
  • 这可以简化为,return !keycheck;
  • 好的,谢谢。我非常专注于解决递归问题,以至于我没有注意到这一点。

标签: java recursion brute-force


【解决方案1】:

您的代码中存在许多问题。

首先,isPermutation 的方法是错误的:你循环到 key.length2 当你应该循环到 9 a.length !方法应该是:

public static boolean isPermutation(int[] a) {
    int[][] key = new int[2][a.length];
    key[0] = a;
    for (int i = 0; i < a.length; i++) {
        key[1][i] = 0;
    }
    for (int i = 0; i < a.length; i++) {
        if (a[i] > 0) {
            key[1][a[i] - 1]++;
        }
    }
    boolean keycheck = false;
    for (int i = 0; i < a.length; i++) {
        if (key[1][i] > 1) {
            keycheck = true;
            break;
        }
    }
    if (keycheck == true) {
        return false;
    } else {
        return true;
    }
}

正如 Patrick J Abare II 所说,结尾真的应该是return ! keycheck;

接下来您尝试使用蛮力,但永远不要回溯solveteilsolve 这对方法应该处理任何级别的不可接受的值,应该是:

public static int[][] solve(int[][] a) {
    int[] freeslot = findfreeslot(a);
    int f1 = freeslot[0];
    int f2 = freeslot[1];
    if (f1 == -1) {
        return a;
    }
    a = teilsolve(f1, f2, a);
    return a;
}

public static int [][] teilsolve(int f1, int f2, int[][] a) {
    int [][] temp2;
    int[][] temp = new int[a.length][a[0].length];
    for (int y = 0; y < a.length; y++) {
        for (int z = 0; z < a[0].length; z++) {
            temp[y][z] = a[y][z];
        }
    }
    for (int i = 1; i < 10; i++) {
        temp[f1][f2] = i;
        boolean valide = isValid(temp);
        if (valide) {
            temp2 = solve(temp);
            if (temp2 != null) {return temp2;}
        }
    }
    return null;
}

这样,程序返回:

4 5 3 9 2 1 6 8 7 
9 2 7 3 6 5 8 4 1 
2 3 1 8 9 6 4 7 5 
5 4 8 1 7 2 9 3 6 
7 6 9 5 3 4 1 2 8 
1 9 6 7 4 8 2 5 3 
3 7 2 6 8 9 5 1 4 
8 1 4 2 5 3 7 6 9 
6 8 5 4 1 7 3 9 2 

【讨论】:

  • 哇,感谢您的回复!你当然是对的。我按照你说的方式实现了它,但是对于给定的数独,solve 方法返回 null。
  • 你确定吗?对我来说它可以工作,但需要很长时间(在 I3 处理器上 20 秒,在 VM 上几乎 2 分钟......)
  • 是的,我真的不知道为什么。我只是执行上面指定的主要方法并尝试将其打印出来。它给了我一个 NullPointerException。
  • 您知道问题出在哪里吗?我想我真的很接近完成我的代码:(
  • @PaulKukowski 我在另一个盒子上试过了:刚刚复制了你的代码并从我的答案中替换了 3 种方法。它给了我正确的结果,所以我不明白如果你这样做,为什么你会得到 NPE。
【解决方案2】:

只是一些一般性建议:

我认为您迷路了,因为老实说,该代码一团糟。

所以,第一个建议:清理该代码。只需将您的 isPermutation(int[]) 方法与以下方法进行比较 - 哪个更易于阅读?如果您甚至无法阅读,更不用说理解它,您就无法真正弄清楚为什么您的代码不起作用。请注意,内联 cmets 非常冗长,但仅对变量使用有意义的名称会有很大帮助。

/**
 * Checks whether the array is a valid permutation of all natural numbers within its length.
 * 
 * @param lineToCheck
 *            a line in sudoku, regardless of direction
 * @return whether the line is valid
 */
public static boolean isPermutation(int[] lineToCheck) {
    // numbersPerLine - will usually be nine, could be anything
    int numbersPerLine = lineToCheck.length;

    // for every number that should exist in the line...
    for (int currentExpectedNumber = 1; currentExpectedNumber <= numbersPerLine; currentExpectedNumber++) {
        boolean foundCurrentNumber = false;

        // ...check whether it is anywhere in the line.
        for (int numberInLine : lineToCheck) {
            if (currentExpectedNumber == numberInLine) {
                // great, we found the number, so check the next one
                foundCurrentNumber = true;
                break;
            }
        }
        if (!foundCurrentNumber) {
            /*
             * if we got here, the expected number could not be found anywhere in the line, so the line is NOT a valid permutation
             */
            return false;
        }
    }
    // all numbers were found, so we do in fact have a valid permutation
    return true;
}

下一个建议: 弄清楚你到底想做什么。你可能知道有几个Sudoku solving algorithms。将您尝试实现的算法的步骤与您的代码执行的步骤对齐。您应该能够浏览整个过程并发现差异。处理这些。

【讨论】:

  • 好的,谢谢你的回复。我将尝试清理我的代码/重写它,然后看看它是否有效。非常感谢您的帮助!
  • 创建一个int[10] array 并仅在一个小循环if array[index] != 0 return false 内检查不是更有效吗?或者在@array[index] 中输入一个值?
  • 有很多方法可以提高速度/内存效率。在这一点上,我们希望让它发挥作用。在那之后,让它快速工作是一种选择。考虑到问题/应用的范围有限,这可能不值得。
  • 代码没有真正优化,但问题不存在。有一个愚蠢的错误,以及一个错误的回溯实现。
  • 好的,现在我开始工作了。再次感谢您的帮助!
猜你喜欢
  • 2013-04-05
  • 1970-01-01
  • 2011-05-01
  • 2015-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多