Løs N-dronninger-problemet: Java & C++ kodeeksempler


Utforskning av N-Dronning-problemet med Tilbaketrekking i Java og C++

Innledning

N-Dronning-problemet, en velkjent utfordring innenfor informatikk, omhandler strategisk utplassering av N dronninger på et NxN-sjakkbrett. Målet er å sikre at ingen av dronningene truer hverandre – verken horisontalt, vertikalt eller diagonalt. For å håndtere denne oppgaven kreves en systematisk tilnærming som evaluerer alle mulige dronningplasseringer, samtidig som ugyldige alternativer elimineres. Tilbaketrekking, en kraftfull algoritme, viser seg å være ideell for å løse dette problemet, og denne artikkelen vil gå nærmere inn på hvordan den implementeres i både Java og C++.

Metodikk

Tilbaketrekking fungerer som en rekursiv algoritme. Den starter med et tomt sjakkbrett, og legger deretter til dronninger én etter én. Hver gang en dronning plasseres, sjekker algoritmen om den nåværende konfigurasjonen er gyldig, det vil si om ingen dronninger truer hverandre. Hvis plasseringen er gyldig, fortsetter algoritmen å utforske dette sporet ved å plassere en ny dronning. Hvis plasseringen er ugyldig, trekker algoritmen seg tilbake (backtracker) og prøver et annet spor.

Denne prosessen gjentas rekursivt, enten til en gyldig løsning er funnet, eller alle mulige plasseringer er blitt vurdert. Hvis en gyldig løsning oppdages, returnerer algoritmen denne plasseringen. Ellers indikerer den at ingen løsning finnes.

Java-implementering

Implementasjonen i Java bruker en todimensjonal matrise for å representere sjakkbrettet og boolean-arrayer for å holde oversikt over brukte kolonner og diagonaler. Den rekursive metoden `solveNQueens` forsøker å plassere dronningene rad for rad.

import java.util.Arrays;

public class NQueens {

    public static void main(String[] args) {
        int n = 4;
        int[][] solution = solveNQueens(n);
        if (solution != null) {
            printSolution(solution);
        } else {
            System.out.println("Ingen løsning");
        }
    }

    private static int[][] solveNQueens(int n) {
        int[][] board = new int[n][n];
        Arrays.fill(board, -1);
        boolean[] colUsed = new boolean[n];
        boolean[] diag1Used = new boolean[2 * n - 1];
        boolean[] diag2Used = new boolean[2 * n - 1];
        return solveNQueens(board, colUsed, diag1Used, diag2Used, 0);
    }

    private static int[][] solveNQueens(int[][] board, boolean[] colUsed, boolean[] diag1Used, boolean[] diag2Used, int row) {
        if (row == board.length) {
            return board;
        }
        for (int col = 0; col < board.length; col++) {
            if (!colUsed[col] && !diag1Used[row + col] && !diag2Used[row - col + board.length - 1]) {
                board[row][col] = 1;
                colUsed[col] = true;
                diag1Used[row + col] = true;
                diag2Used[row - col + board.length - 1] = true;
                int[][] solution = solveNQueens(board, colUsed, diag1Used, diag2Used, row + 1);
                if (solution != null) {
                    return solution;
                }
                board[row][col] = 0;
                colUsed[col] = false;
                diag1Used[row + col] = false;
                diag2Used[row - col + board.length - 1] = false;
            }
        }
        return null;
    }

    private static void printSolution(int[][] solution) {
        for (int i = 0; i < solution.length; i++) {
            for (int j = 0; j < solution[i].length; j++) {
                System.out.print(solution[i][j] + " ");
            }
            System.out.println();
        }
    }
}

C++-implementering

C++-versjonen bruker vektorer for å representere sjakkbrettet og for å spore brukte rader, kolonner og diagonaler. Den rekursive funksjonen `solveNQueens` utfører tilbaketrekkingslogikken.

#include <iostream>
#include <vector>

using namespace std;

vector<vector<int>> board;
vector<bool> rowUsed;
vector<bool> colUsed;
vector<bool> diag1Used;
vector<bool> diag2Used;
vector<vector<int>> solution;


bool isValid(int row, int col) {
    return !rowUsed[row] && !colUsed[col] && !diag1Used[row + col] && !diag2Used[row - col + board.size() - 1];
}


bool solveNQueens(int row) {
    if (row == board.size()) {
        solution = board;
        return true;
    }

    for (int col = 0; col < board.size(); col++) {
        if (isValid(row, col)) {
            board[row][col] = 1;
            rowUsed[row] = true;
            colUsed[col] = true;
            diag1Used[row + col] = true;
            diag2Used[row - col + board.size() - 1] = true;

            if (solveNQueens(row + 1)) {
                return true;
            }

            board[row][col] = 0;
            rowUsed[row] = false;
            colUsed[col] = false;
            diag1Used[row + col] = false;
            diag2Used[row - col + board.size() - 1] = false;
        }
    }
    return false;
}

int main() {
    int n;
    cout << "Enter the size of the chessboard: ";
    cin >> n;

    board.resize(n, vector<int>(n, 0));
    rowUsed.resize(n, false);
    colUsed.resize(n, false);
    diag1Used.resize(2 * n - 1, false);
    diag2Used.resize(2 * n - 1, false);

    if (solveNQueens(0)) {
        cout << "Solution:\n";
        for (int i = 0; i < board.size(); i++) {
            for (int j = 0; j < board[i].size(); j++) {
                cout << board[i][j] << " ";
            }
            cout << endl;
        }
    } else {
        cout << "No solution found" << endl;
    }

    return 0;
}

Konklusjon

Tilbaketrekking fremstår som en robust algoritme for å løse N-Dronning-problemet. Dens systematiske tilnærming sikrer at alle mulige kombinasjoner blir vurdert, og tilbaketrekningsmekanismen begrenser evalueringen til kun gyldige dronningplasseringer. Implementeringene i både Java og C++ viser hvordan algoritmen effektivt kan løse dette klassiske problemet.

Ofte stilte spørsmål

  • Hva er N-Dronning-problemet?
    N-Dronning-problemet går ut på å plassere N dronninger på et NxN-sjakkbrett på en slik måte at ingen av dem truer hverandre.
  • Hvordan fungerer tilbaketrekking i sammenheng med N-Dronning-problemet?
    Tilbaketrekking starter med et tomt brett og legger til dronninger én etter én, evaluerer gyldigheten av hver plassering, og trekker seg tilbake ved ugyldige alternativer.
  • Hvorfor er tilbaketrekking en effektiv metode for dette problemet?
    Algoritmen undersøker alle potensielle konfigurasjoner på en strukturert måte, noe som garanterer at en løsning blir funnet dersom den eksisterer.
  • Hvilke varianter av tilbaketrekking finnes?
    Dybde-først-søk, bredde-først-søk og iterativ fordypning er eksempler på varianter av tilbaketrekking som kan benyttes.
  • Eksisterer det andre algoritmer for å løse N-Dronning-problemet?
    Ja, algoritmer som branch and bound og simulated annealing er også aktuelle alternativer.
  • Hva er kompleksiteten ved tilbaketrekking?