ยินดีต้อนรับการเพิ่มประสิทธิภาพอัลกอริทึมตัวสร้าง Sudoku

ฉันสร้างอัลกอริธึม DFS แบบเรียกซ้ำเพื่อสร้าง/แก้ไขบอร์ดซูโดกุใน Java แต่จะใช้เวลานานกว่าจะยุติ และยินดีรับฟังคำอธิบาย/การปรับให้เหมาะสม ฉันจินตนาการไม่ออกเลยว่าการสร้างกระดานซูโดกุจะใช้เวลานานขนาดนี้ โดยเฉพาะอย่างยิ่งกับแอปทั้งหมดที่อยู่รอบๆ (แม้ว่าแอปเหล่านั้นอาจมีฐานข้อมูลก็ตาม)

โดยพื้นฐานแล้ว ฉันจะสำรวจเซลล์ทั้งหมดเพื่อดูว่ามี [1-9] ใดที่จะเป็นไปตามข้อจำกัดของซูโดกุ และย้อนรอยกิ่งก้านทางตันหรือไม่ เพื่ออนุรักษ์หน่วยความจำและหลีกเลี่ยงการคัดลอกอาเรย์ 2 มิติที่ทำหน้าที่เป็นบอร์ดพร้อมกับการเรียกใช้เมธอดแบบเรียกซ้ำแต่ละครั้ง (และอาจมี 81*9! เหลืออยู่ในแผนผังนั้น ถ้าจำไม่ผิด...) ฉันจึงสร้าง 2D เมทริกซ์ของสแต็กจำนวนเต็ม ซึ่งองค์ประกอบจะถูกผลักทุกครั้งที่สำรวจสาขา และแตกออกมาหากเป็นจุดสิ้นสุด

ด้านล่างนี้คือการดำเนินการ ยินดีให้คำแนะนำเรื่องการเร่งความเร็วครับ ฉันทำสิ่งนี้เป็นแบบฝึกหัดส่วนตัว และฉันสงสัยว่ามีอะไรที่ดีกว่าแบบไม่มีสัญญาณกำกับอยู่หรือไม่

หวังว่ามันจะไม่แย่มากอ่านด้านล่าง .. ขอบคุณ!

1) การใช้อัลกอริทึม: (โปรดทราบว่าค่าอยู่ในอาร์เรย์ "ที่สับสน" ของ [1-9] เพื่อสร้างบอร์ดที่ไม่ซ้ำใคร)

/**
 * Provides one solution to a board with an initial configuration, or <code>null</code> if there is none.
 * The search is randomized, s.t. the algorithm can serve to populate an empty board. 
 * 
 * @param initial The initial board given to solve.
 * @return The fully solved board, or null if no solution found.
 */
public static int[][] solveBoard (int[][] initial){
    return solveBoard(new StackedBoard(initial), 0, 0);
}

private static int[][] solveBoard (StackedBoard board, int xPos, int yPos){

    // base case - success
    int remaining = 81;
    for (int x = 0; x < 9; x++){
        for (int y = 0; y < 9; y++){
            if (board.peekAt(x, y) != Board.EMPTY){
                remaining--;
            }
        }
    }
    if (remaining == 0){
        return board.flatView();// the only creation of an array.
    }

    // recursive case
    for (int x = 0; x < 9; x++){
        for (int y = 0; y < 9; y++){
            if (board.peekAt(x, y) == Board.EMPTY){
                for (int val : getJumbledRandomVals()){
                    if (isMoveLegal(board, x, y, val)){
                        board.pushAt(x, y, val);
                        int[][] leafBoard = solveBoard(board, x, y);
                        if (leafBoard != null) {
                            return leafBoard;
                        }
                    }
                }
            }
        }
    }

    // base case - dead branch
    board.popAt(xPos, yPos);
    return null;
}

2) การใช้งาน StackedBoard:

/**
 * Represents square boards with stacked int elements.
 */
class StackedBoard {

    ArrayList<ArrayList<Stack<Integer>>> xaxis = new ArrayList<ArrayList<Stack<Integer>>>();

    /**
     * 
     * @param init A square array - both dimensions of equal length, or <code>null</code> if no initialization.
     */
    public StackedBoard (int[][] init) {
        for (int i = 0; i < 9; i++){
            ArrayList<Stack<Integer>> yaxis = new ArrayList<Stack<Integer>>();
            xaxis.add(yaxis);

            for (int j = 0; j < 9; j++){
                Stack<Integer> stack = new Stack<Integer>();
                yaxis.add(stack);
            }
        }

        if (init != null){
            for (int x = 0; x < init.length; x++){
                for (int y = 0; y < init.length; y++){
                    getStackAt(x, y).push(init[x][y]);
                }
            }   
        }
    }

    public Stack<Integer> getStackAt (int x, int y){
        return xaxis.get(x).get(y);
    }

    public int peekAt (int x, int y){
        return getStackAt(x, y).peek();
    }

    public void pushAt (int x, int y, int value){
        getStackAt(x, y).push(value);
    }

    public Integer popAt (int x, int y){
        try {
            return getStackAt(x, y).pop();  
        } catch (EmptyStackException e){
            // shhhhh!
            return Board.EMPTY;
        }

    }

    /**
     * Flat view of the stacked-board; peek of the top elements.
     */
    public int[][] flatView (){
        int[][] view = new int[xaxis.size()][xaxis.size()];

        for (int x = 0; x < xaxis.size(); x++){
            for (int y = 0; y < xaxis.size(); y++){
                view[x][y] = getStackAt(x, y).peek();
            }
        }

        return view;
    }
}

3) การใช้ฟังก์ชันข้อจำกัด:

/**
 * Is the move legal on the suggested board?
 * 
 * @param board The board.
 * @param x The X coordinate, starts with 0.
 * @param y The Y coordinate, starts with 0.
 * @param value The value.
 * @return <code>true</code> iff the move is legal.
 */
private static boolean isMoveLegal (StackedBoard board, int x, int y, int value){
    // by value
    if (1 > value || value > 9){
        return false;
    }

    // by column
    for (int i = 0; i < 9; i++){
        if (board.peekAt(i, y) == value){
            return false;
        }
    }

    // by row
    for (int i = 0; i < 9; i++){
        if (board.peekAt(x, i) == value){
            return false;
        }
    }

    // by lil square
    int lowerX = x < 3 ? 0 : (x < 6 ? 3 : 6);
    int upperX = lowerX + 2;
    int lowerY = y < 3 ? 0 : (y < 6 ? 3 : 6);
    int upperY = lowerY + 2;

    for (int i = lowerX; i <= upperX; i++){
        for (int j = lowerY; j <= upperY; j++){
            if (board.peekAt(i, j) == value){
                return false;
            }
        }
    }

    return true;
}

person vivri    schedule 01.11.2013    source แหล่งที่มา
comment
ดีขึ้นแบบไม่แสดงอาการ?   -  person Paul Draper    schedule 01.11.2013
comment
อนุญาตให้ฉันเสียบโพสต์บล็อกของตัวเองสักครู่ ซูโดกุสามารถแก้ไขได้อย่างรวดเร็วโดยครอบคลุม: gieseanw.wordpress.com /2011/06/16/การแก้-ซูโดกุ-มาเยือนแล้ว   -  person AndyG    schedule 01.11.2013
comment
คำถามนี้น่าจะอยู่ใน codereview.stackexchange.com   -  person asermax    schedule 01.11.2013


คำตอบ (1)


หากคุณเต็มใจที่จะเลี้ยวซ้ายโดยสิ้นเชิง มีอัลกอริธึมที่ดีกว่ามากสำหรับการสร้าง / แก้ซูโดกุ อัลกอริธึมลิงก์การเต้นรำของ Don Knuth เป็นที่รู้กันว่าเก่งมากในการแจกแจงวิธีแก้ปัญหา Sudoku ทั้งหมดอย่างรวดเร็ว (เมื่อ ใช้ถ้อยคำใหม่เป็นตัวอย่างของปัญหาหน้าปกที่แน่นอน) และมักใช้เป็นอัลกอริทึมหลักใน นักแก้ปัญหา Sudoku และมันก็คุ้มค่าที่จะลองดู ต้องใช้ยิมนาสติกพอยน์เตอร์/อ้างอิงจำนวนมาก แต่การเขียนโค้ดค่อนข้างสั้น

หากคุณต้องการยึดแนวทางที่มีอยู่ การเพิ่มประสิทธิภาพที่เป็นประโยชน์ประการหนึ่งคือเลือกเซลล์ที่มีข้อจำกัดมากที่สุดเป็นค่าถัดไปที่จะกรอก ซึ่งอาจทำให้เกิด "การบังคับย้าย" ต่อเนื่องกันซึ่งจะช่วยลดขนาดเซลล์ของคุณ พื้นที่ค้นหาแม้ว่าจะเป็นเพียงฮิวริสติกก็ตาม

หวังว่านี่จะช่วยได้!

person templatetypedef    schedule 01.11.2013
comment
ขอบคุณ! ฉันจะดูวิธีแก้ปัญหาทั้งสอง! - person vivri; 01.11.2013