Nルークス問題ではn = 8のためにStackOverflowのエラーを回避する方法

あなたはYalkabovを支払います:

私が解決したいNにおけるルックスの問題Nは、N xは最大で再帰を使用してボードN = 8私のコードのために罰金を動作N = 234567しかしときNは = 8、それは最初の行から始まるので、多くの可能な結果を与える1 0 0 0 0 0 0 0次に与えるstackoverflowのエラーの最初の行から始まる他の可能な結果を確認する前に、0 1 0 0 0 0 0 0

私のような一般的な再帰について知っているフィボナッチ数列階乗など、と私はそれらを追跡することができます。それから私は、再帰をバックトラックと呼ばれる再帰の新しい形に出くわしました。それから私は、再帰のこのフォームの背後にあるロジックを学び、いくつかの擬似コードアルゴリズムを読むことをsarted。再帰のAcuallyこの形式は、通常の再帰よりも構築するためには少し難しいように思えたのでした。

public class NRooks {
/**
 * In this code r = which row, c = which column.
 * lastY method just returns column c of last placed rook in
 * a given row r in order to remove it.
 * row.length, col.length, board.length have no special meaning. They all
 * equal to the dimension of board N.
 * main() method always initiates first row(r = 0). Therefore in main()
 * method r remains 0 and c changes as you can see in putRook(0, i). 
 * So solve() method always begins from second row(r = 1).
 */

private static int found = 0;
private static int[][] board;
private static int[] row;
private static int[] col;

public static void putRook(int r, int c) {
    board[r][c] = 1;
    row[r]  = 1;
    col[c]  = 1;
}

public static void removeRook(int r, int c) {
    board[r][c] = 0;
    row[r]  = 0;
    col[c]  = 0;
}

public static boolean isValid(int r, int c) {
    if (row[r] == 0 && col[c] == 0) return true;
    return false;
}

public static void showBoard() {
    for (int r = 0; r < board.length; r++) {
        for (int c = 0; c < board.length; c++) {
            System.out.print(board[r][c] + " ");
        }
        System.out.println();
    }
    System.out.println();
}

public static int lastY(int r) {
    for (int j = 0; j < board.length; j++) {
        if (board[r][j] == 1) return j;
    }
    return -1;
}


public static boolean solve(int r, int c) {
    int last;

    if (r == 0) return false;

    if (r == col.length) {
        found++;
        /**
         * When I dont include below printline statement my code 
         * works fine until N = 7 then gives SO error.
         * But When I include this print statement in order
         * to print number of results my code works fine until
         * N = 6 then gives SO error
         */
        //System.out.println("Found: " + found);
        showBoard();
        r--;
        last = lastY(r);
        removeRook(r, last);
        c = last + 1;
    }

    for (int j = c; j < row.length; j++) {
        if (isValid(r, j)) {
            putRook(r, j);
            return solve(r + 1, 0);
        }
    }

    last = lastY(r - 1);
    removeRook(r - 1, last);
    return solve(r - 1, last + 1);
}

public static void main(String[] args) {
    int n = Integer.parseInt(args[0]);
    board = new int[n][n];
    row = new int[n];
    col = new int[n];

    for (int i = 0; i < row.length; i++) {
        boolean finished; // not important
        putRook(0, i);
        finished = solve(1, 0);
        if (finished) System.out.println("============"); // ignore this too
    }
}
}

stackoverflowのために再帰呼び出しを含む行を指し()解決法。

注:私は知っているC、Javaおよび基本的なデータ抽象化の構文などを。私は私のこのレベルでこのコードを書いたJavaの

私はこの問題とNクイーン問題自分自身を解決したいです。両方の数学とアルゴリズムそこに、これらの問題に非常に多くのソリューションが、ありますので。そして、私は高度に興味はないのJava データ抽象化、今の事。
私だけのようなもの上記の私のコードスニペットに関するいくつかのアドバイスをしたいです

  • あなたのバックトラッキングアルゴリズムは効率的ではありません。(そうstraightfoward)
  • あなたはいくつか使用する必要があるのJavaデータを効率的にこの問題を解決するために、物事を抽象化。
  • あなたは次のように再帰の別のフォームを使用する必要が末尾再帰(私もこれについて聞きました。)
  • ....
エルダー:

あなたがスタックオーバーフローエラーを取得している理由の主な問題は、あなたの再帰が構成されている方法です。モーメントがsolveで呼び出されるmainメソッド、それは深く深く再帰的に保持します。実際には、その呼び出しのすべてが単一に数千の呼び出しの深チェーンを形成します。N = 7の場合、3193回のネストされた呼び出しが(私はこれを確認するためにカウンタを追加)があります。N = 8の場合、それは私のマシン上でスタックをオーバーフローするまで5Kについての再帰呼び出しを実行-私は、スタックサイズは、デフォルトではかなり小さいですね。

このように、n個のより高い値のための仕事にこれを取得するには、単鎖など、すべての再帰呼び出しを行わない方法で再構築にあなたの再帰を必要とします。私はそれが実際に後戻りすることはありませんので、あなたの現在のソリューションは本当にバックトラッキングされていないと主張することができます。私は単純な問題に手段をバックトラック何説明してみましょう。nの値を知ることに依存せずに、プログラムであなたは長さのすべてのバイナリ文字列を印刷したいのn = 3(「111」から「000」)としましょう。このため、実装は次のようなものが考えられます。

def build_binary_string(current_prefix, chars_left):
  if chars_left == 0:
    print current_prefix
    return
  build_binary_string(current_prefix + 'a', chars_left - 1)
  build_binary_string(current_prefix + 'b', chars_left - 1)

build_binary_string("", 3)

興味深いの(!バックトラック)は、現時点では起こらbuild_binary_string引数(「00」、1)で呼び出されます。

  • build_binary_string("000", 0) すぐに、呼び出された「000」を印刷し、リターンされます
  • 私たちは、背面にあるbuild_binary_string("00", 1)権利について実行するために、関数呼び出しbuild_binary_string(current_prefix + 'b', chars_left - 1)
  • build_binary_string("001", 0) すぐに、呼び出された「001」を印刷し、リターンされます

制御フローから返されたその時点build_binary_string("000", 0)までbuild_binary_string("00", 1)、それはコールバックトラックされた別の関数を作ることにしました。再帰の深さが3を超えたことがないことに注意してください。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=214193&siteId=1