ist eine vielseitige Technik, um den Lösungsraum verschiedener Arten von Datenwissenschaftsproblemen zu untersuchen und Kandidatenlösungen schrittweise zu konstruieren – ein bisschen wie das Navigieren eines Labyrinths. In diesem Artikel werden wir kurz das Konzept des Backtracking durchgehen, bevor wir in ein paar intuitive, praktische Beispiele in Python eintauchen.

Notiz: Alle Beispiel -Code -Ausschnitte in den folgenden Abschnitten wurden vom Autor dieses Artikels erstellt.

Konzeptuelle Übersicht

Auf hohem Niveau beinhaltet die Backtracking-Technik eine Schritt-für-Schritt-Untersuchung des Lösungsraums eines Rechenproblems (normalerweise ein Downside, das als Begrenzungszufriedenheit oder kombinatorische Optimierung eingerahmt werden kann). Bei jedem Schritt in der Erkundung gehen wir auf verschiedenen Wegen fort und überprüfen, ob die Problembeschränkungen im Laufe der Zeit erfüllt sind.

Wenn wir während unserer Erkundung eine gültige Lösung treffen, notieren wir uns. An diesem Punkt können wir die Suche beenden, wenn unser Downside nur eine gültige Lösung finden muss. Wenn das Downside das Finden mehrerer (oder aller) möglicher Lösungen erfordert, können wir weiterhin Erweiterungen der zuvor entdeckten Lösung untersuchen.

Wenn jedoch zu irgendeinem Zeitpunkt die Problembeschränkungen verletzt werden, sind wir Backtrack; Dies bedeutet, auf den letzten Punkt unserer Suche zurückzukehren, an dem eine teilweise Lösung gebaut worden battle (und wo gültige Lösungen immer noch möglich schienen) und unsere Suche auf einem anderen Weg als dort fortzusetzen. Dieser Ahead-and-Backward-Prozess der Erkundung kann nach Bedarf fortgesetzt werden, bis der gesamte Lösungsraum untersucht und alle gültigen Lösungen untersucht werden.

Praktische Beispiele

Lösen eines Sudoku

Ein Sudoku -Puzzle ist ein klassisch Operationsforschung Zu Kryptographie. Die Standardversion des Puzzles besteht aus einem 9-mal-9-Raster, das aus neun nicht überlappenden 3-mal-3-Sub-Grids (oder Blöcken) besteht. In der Startkonfiguration des Puzzles sind einige der 81 Zellen im Raster mit Ziffern von 1 bis 9 vorgefüllt. Um das Puzzle zu vervollständigen, müssen die verbleibenden Zellen mit Ziffern von 1 bis 9 gefüllt werden, während die folgenden Einschränkungen einhalten: Keine Zeile, Säule oder 3-mal-3-Block.

Der folgende Python-Code zeigt, wie ein Sudoku-Solver mithilfe von Backtracking implementiert wird, zusammen mit einer Comfort-Funktion für das ziemlich drückende Netz. Beachten Sie, dass der Solver erwartet, dass leere Zellen mit Nullen bezeichnet (oder initialisiert) werden.

from copy import deepcopy

def is_valid(board, row, col, num):
    # Verify if num isn't within the present row or column
    for i in vary(9):
        if board(row)(i) == num or board(i)(col) == num:
            return False
    # Verify if num isn't within the 3-by-3 block
    start_row, start_col = 3 * (row // 3), 3 * (col // 3)
    for i in vary(start_row, start_row + 3):
        for j in vary(start_col, start_col + 3):
            if board(i)(j) == num:
                return False
    return True

def find_empty_cell(board):
    # Discover the subsequent empty cell (denoted by 0)
    # Return (row, col) or None if puzzle is full
    for row in vary(9):
        for col in vary(9):
            if board(row)(col) == 0:
                return row, col
    return None

def solve_board(board):
    empty = find_empty_cell(board)
    if not empty:
        return True  # Solved
    row, col = empty
    for num in vary(1, 10):
        if is_valid(board, row, col, num):
            board(row)(col) = num
            if solve_board(board):
                return True
            board(row)(col) = 0  # Backtrack
    return False

def solve_sudoku(start_state):
    board_copy = deepcopy(start_state)  # Keep away from overwriting authentic puzzle
    if solve_board(board_copy):
        return board_copy
    else:
        elevate ValueError("No resolution exists for the given Sudoku puzzle")

def print_board(board):
    for i, row in enumerate(board):
        if i > 0 and that i % 3 == 0:
            print("-" * 21)
        for j, num in enumerate(row):
            if j > 0 and j % 3 == 0:
                print("|", finish=" ")
            print(num if num != 0 else ".", finish=" ")
        print()

Nehmen wir nun an, wir geben ein Sudoku -Puzzle ein, initialisieren leere Zellen mit Nullen und führen den Löser wie folgt durch:

puzzle = (
    (5, 0, 0, 0, 3, 0, 0, 0, 7),
    (0, 0, 0, 4, 2, 7, 0, 0, 0),
    (0, 2, 0, 0, 6, 0, 0, 4, 0),
    (0, 1, 0, 0, 9, 0, 0, 2, 0),
    (0, 7, 0, 0, 0, 0, 0, 5, 0),
    (4, 0, 6, 0, 0, 0, 7, 0, 1),
    (0, 4, 2, 0, 7, 0, 6, 1, 0),
    (0, 0, 0, 0, 4, 0, 0, 0, 0),
    (7, 0, 0, 9, 5, 6, 0, 0, 2),
)

resolution = solve_sudoku(puzzle)
print_board(resolution)

Der Löser erzeugt die folgende Lösung in Millisekunden:

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

Ein mathematisches olympiade Downside knacken

Mathe Olympiaden sind Wettbewerbe für Studenten vor der Universität und bestehen aus schwierigen mathematischen Problemen, die unter zeitgesteuerten Bedingungen ohne den Einsatz von Taschenrechnern gelöst werden müssen. Da die systematische Erforschung des vollständigen Lösungsraums für solche Probleme in der Regel nicht möglich ist, stützen sich erfolgreiche Lösungsansätze in der Regel auf analytisches Denken und mathematischer Einfallsreichtum, wodurch explizite und implizite Einschränkungen aus der Problemanweisung ausgewählt werden, um die Suche des Lösungsraums zu optimieren. Einige Probleme haben mit der Begrenzungszufriedenheit und der kombinatorischen Optimierung zu tun, auf die wir auch in den Datenwissenschaftsproblemen in der Industrie stoßen (z. B. überprüft, ob ein Pfad zu einem bestimmten Ziel vorhanden ist und alle möglichen Wege zu einem Ziel finden und den kürzesten Weg zu einem Ziel finden). Selbst wenn ein cleverer Ansatz für mathematische Lösungen für ein bestimmtes olympiade Downside vorhanden ist, kann es lehrreich sein, andere verallgemeinerbare Ansätze (z.

Betrachten Sie zum Beispiel die folgendes Downside Das erschien in Runde 1 der britischen mathematischen Olympiade im November 2018: „Eine Liste von fünf zweistelligen positiven Ganzzahlen ist in zunehmender Reihenfolge auf einer Tafel geschrieben. Jede der fünf Zahlen ist ein Vielfaches von 3, und jede Ziffer 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 erscheint genau einmal in der Blackboard. In wie vielen Arten kann dies möglich sein?

In diesem Fall lautet die Lösung für das obige Downside 288. In dem folgenden Video wird ein Lösungsansatz erläutert, bei dem einige wichtige explizite und implizite Merkmale der spezifischen Problemanweisung geschickt ausgenutzt werden (z. B. muss die Lösung als geordnete Liste angezeigt werden und eine Zahl ist ein Vielfaches von 3, wenn die Summe der Ziffern auch ein Vielfaches von 3 ist).

https://www.youtube.com/watch?v=lntlztjpmza

Der folgende Python -Code zeigt, wie Backtracking verwendet werden kann, um das Downside zu lösen:

def is_valid_combination(numbers):
    # Checks if every digit from 0-9 seems precisely as soon as in a listing of numbers
    digits = set()
    for quantity in numbers:
        digits.replace(str(quantity))
    return len(digits) == 10

def find_combinations():
    multiples_of_3 = (i for i in vary(12, 100) 
                        if i % 3 == 0 and '0' not in str(i)(0))
    valid_combinations = ()
    def backtrack(begin, path):
        if len(path) == 5:
            if is_valid_combination(path):
                valid_combinations.append(tuple(path))
            return
        for i in vary(begin, len(multiples_of_3)):
            backtrack(i + 1, path + (multiples_of_3(i)))
    backtrack(0, ())
    return valid_combinations
    
print(f"Resolution: {len(find_combinations())} methods")

Die Funktion is_valid_combination() Gibt die wichtige Einschränkung an, die für jede gültige 5-Nummer-Liste gelten muss, die während der Erforschung des Suchraums entdeckt wurde. Die Liste multiples_of_3 Funktioniert die Kandidatennummern, die möglicherweise in einer gültigen 5-Nummer-Liste angezeigt werden. Die Funktion find_combinations() Wendet Backtracking an, um alle einzigartigen 5-Quantity-Kombinationen von auszuprobieren multiples_of_3.

Die Funktion is_valid_combination() und das Listenverständnis, das zum Generieren verwendet wird multiples_of_3 kann geändert werden, um eine breite Palette ähnlicher Probleme zu lösen.

Jenseits des Backtracking

Wie wir gesehen haben, ist Backtracking eine einfache, aber leistungsstarke Technik zur Lösung verschiedener Arten von Einschränkungen Zufriedenheit und kombinatorischen Optimierungsproblemen. Es gibt jedoch auch andere Techniken wie die Tiefe-First-Suche (DFS) und die dynamische Programmierung (DP) und können auf der Oberfläche ähnlich aussehen. Wann ist es additionally sinnvoll, Backtracking anstelle dieser anderen Techniken zu verwenden?

Backtracking kann als eine strategischere Type von DFS angesehen werden, bei der die Einschränkungsprüfung ein Kernmerkmal jedes Entscheidungsschritts ist und ungültige Pfade frühzeitig aufgegeben werden können. In der Zwischenzeit kann DP für Probleme verwendet werden, die zwei Eigenschaften aufweisen: überlappende Unterprobleme und an optimale Unterstruktur. Ein Downside hat überlappende Unterprobleme, wenn dieselben Unterprobleme mehrmals gelöst werden müssen, während das größere Downside gelöst wird. Das Speichern und Wiederverwenden der Ergebnisse der wiederkehrenden Unterprobleme (z. B. unter Verwendung einer Memoisierung) ist ein Schlüsselmerkmal von DP. Darüber hinaus hat ein Downside eine optimale Unterstruktur, wenn eine optimale Lösung für das Downside konstruiert werden kann, indem auf optimalen Lösungen für seine Teilprobleme aufgebaut werden.

Betrachten Sie nun das N-Queens-Downside, das untersucht, wie man platziert N Königinnen auf einem N-von-N Schachbrett, so dass sich keine zwei Königinnen angreifen können; Dies ist ein klassisches Downside, das Anwendungen in mehreren realen Szenarien aufweist, in denen das Finden von Lösungen ohne Konflikte von entscheidender Bedeutung ist (z. B. Ressourcenzuweisung, Planung, Schaltungsdesign und Pfadplanung für Roboter). Das N-Queens-Downside zeigt nicht inhärent überlappende Unterprobleme oder eine optimale Unterstruktur, da Unterprobleme möglicherweise nicht unbedingt gelöst werden müssen, um das Gesamtproblem zu lösen, und die Platzierung von Königinnen in einem Teil der Karte garantiert keine optimale Platzierung für die gesamte Platine. Die inhärente Komplexität des N-Queens-Issues macht es somit weniger geeignet, die Stärken von DP zu nutzen, während die Rückverdünnung auf natürliche Weise mit der Struktur des Issues übereinstimmt.

Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert