N-Königin backtracking in Python: wie die Rückkehr Lösungen, anstatt Sie zu drucken?
def solve(n):
#prepare a board
board = [[0 for x in range(n)] for x in range(n)]
#set initial positions
place_queen(board, 0, 0)
def place_queen(board, row, column):
"""place a queen that satisfies all the conditions"""
#base case
if row > len(board)-1:
print board
#check every column of the current row if its safe to place a queen
while column < len(board):
if is_safe(board, row, column):
#place a queen
board[row][column] = 1
#place the next queen with an updated board
return place_queen(board, row+1, 0)
else:
column += 1
#there is no column that satisfies the conditions. Backtrack
for c in range(len(board)):
if board[row-1][c] == 1:
#remove this queen
board[row-1][c] = 0
#go back to the previous row and start from the last unchecked column
return place_queen(board, row-1, c+1)
def is_safe(board, row, column):
""" if no other queens threaten a queen at (row, queen) return True """
queens = []
for r in range(len(board)):
for c in range(len(board)):
if board[r][c] == 1:
queen = (r,c)
queens.append(queen)
for queen in queens:
qr, qc = queen
#check if the pos is in the same column or row
if row == qr or column == qc:
return False
#check diagonals
if (row + column) == (qr+qc) or (column-row) == (qc-qr):
return False
return True
solve(4)
Schrieb ich Python-code für N-queen problem, und es gibt jede mögliche Lösung, wenn es gefunden wird. Jedoch möchte ich diesen code ändern, so dass es gibt eine Liste mit allen Lösungen(board-Konfigurationen), anstatt Sie zu drucken. Ich habe versucht, ändern Sie den code wie folgt:
def solve(n):
#prepare a board
board = [[0 for x in range(n)] for x in range(n)]
#set initial positions
solutions = []
place_queen(board, 0, 0, solutions)
def place_queen(board, row, column, solutions):
"""place a queen that satisfies all the conditions"""
#base case
if row > len(board)-1:
solutions.append(board)
return solutions
#check every column of the current row if its safe to place a queen
while column < len(board):
if is_safe(board, row, column):
#place a queen
board[row][column] = 1
#place the next queen with an updated board
return place_queen(board, row+1, 0, solutions)
else:
column += 1
#there is no column that satisfies the conditions. Backtrack
for c in range(len(board)):
if board[row-1][c] == 1:
#remove this queen
board[row-1][c] = 0
#go back to the previous row and start from the last unchecked column
return place_queen(board, row-1, c+1, solutions)
Jedoch zurück, sobald die erste Lösung gefunden wird, so solutions
nur darin besteht, eine mögliche board-Konfiguration. Da print vs. return wurde verwirrend für mich für backtracking-algorithmen, ich würde es sehr begrüßen, wenn jemand könnte mir zeigen, wie ändern Sie den obigen code, und der Umgang mit ähnlichen Problemen in der Zukunft.
Ich nehme an, mit einer globalen Variablen arbeiten würde, aber ich habe von irgendwo, dass die Verwendung von globalen Variablen für ein solches problem ist entmutigt. Könnte mir jemand erklären, dieses auch?
EDIT:
def solve(n):
#prepare a board
board = [[0 for x in range(n)] for x in range(n)]
#set initial positions
solutions = list()
place_queen(board, 0, 0, solutions)
return solutions
def place_queen(board, row, column, solutions):
"""place a queen that satisfies all the conditions"""
#base case
if row > len(board)-1:
#print board
solutions.append(deepcopy(board)) #Q1
#check every column of the current row if its safe to place a queen
while column < len(board):
if is_safe(board, row, column):
#place a queen
board[row][column] = 1
#place the next queen with an updated board
return place_queen(board, row+1, 0, solutions) #Q2
else:
column += 1
#there is no column that satisfies the conditions. Backtrack
for c in range(len(board)):
if board[row-1][c] == 1:
#remove this queen
board[row-1][c] = 0
#go back to the previous row and start from the last unchecked column
return place_queen(board, row-1, c+1, solutions) #Q3
Den oben genannten gibt alle Lösungen gefunden, statt Sie zu drucken. Ich habe ein paar mehr Fragen (sicherheitsbezogene Teile markiert sind Q1 und Q2 im obigen code)
- Warum müssen wir
solutions.append(deepcopy(board))
? In anderen Worten, was genau passiert, wenn wir tunsolutions.append(board)
und warum führt das zum anfügen der ersten board, die[[0,0,0,0] ...]
? - Warum laufen wir in ein problem, wenn
return place_queen(board, row+1, 0)
wird ersetzt durchplace_queen(board, row+1, 0)
? Sind wir nicht tatsächlich wieder etwas (oderNone
), aber ohnereturn
die Liste geht aus dem index.
- Wo sind Sie, Druck Sie in die erste snippet?
print board
wenn die aktuelle Zeile überschritten hat, die Größe der Leiterplatte- Zur Beantwortung Ihrer
global
sub-Frage: c2.com/cgi/wiki?GlobalVariablesAreBad - Und
print
vs.return
: stackoverflow.com/questions/7664779/... (bitte nicht mehrere Fragen in einem post, vor allem, wenn man ein Duplikat). - Danke für den link @jonrsharpe aber ich fürchte, das ist nicht das, was ich fordere. Der Unterschied zwischen
print
undreturn
ist ziemlich klar. Was war ärgerlich, mir ist, wenn ich kann leicht drucken Sie das Ergebnis erwartet man von einem, ich habe eine harte Zeit versucht, herauszufinden, wie die Rückgabe einer Reihe von erwarteten Ergebnissen.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Müssen Sie passen Sie Ihren code, um wieder ansetzen, nachdem eine Lösung gefunden ist, wird anstelle der Rückgabe. Möchten Sie nur zurück, wenn Sie finden, sich selbst backtracking aus der ersten Zeile (weil das zeigt, dass Sie erkundet haben jede mögliche Lösung).
Ich denke, das ist am einfachsten zu tun, wenn Sie ändern die Struktur des Codes ein bit-und-Schleife bedingungslos über die Spalten, mit der backtracking-Logik treten, wenn Sie out of bounds, entweder in der Zeile oder Spalte:
Dieser Arbeit wird für kleine boards (wie 4), aber Sie werden hit-Python-Rekursion begrenzen, wenn Sie versuchen, es für größere board-Größen. Python nicht optimieren-out-tail-Rekursion, so ist dieser code baut eine Menge der stack-frames, wie es übergänge von einer Zeile zur anderen.
Glücklicherweise tail-rekursive algorithmen sind in der Regel leicht zu biegen Sie in die iterative algorithmen. Hier ist, wie das getan werden kann, um den obigen code:
Beachten Sie, dass die iterative version braucht nicht genannt zu werden, mit verschiedenen Zeile und Spalte ab der Werte, so dass diese nicht benötigt als Parameter. Ebenso werden der Vorstand und der Ergebnis-Liste-setup kann durchgeführt werden, bevor die Schleife, anstatt in eine helper-Funktion (weitere Reduzierung der Parameter der Funktion).
Etwas mehr Pythonic version dieses wäre ein generator, der
yield
s seine Ergebnisse, statt Sie zu sammeln bis in eine Liste, um die Rückkehr am Ende, aber das erfordert nur ein paar kleine änderungen (nuryield
stattsolutions.append
). Mit einem generator können auch lassen Sie überspringen das kopieren der Vorstand jedes mal, wenn Sie eine Lösung haben, solange Sie können, hängen von der generator die Verbraucher mit dem Ergebnis sofort (oder seine Kopie), bevor es Fortschritte, den generator wieder.Andere Idee wäre, vereinfachen Sie Ihre Geschäftsführung. Sie wissen, dass es immer nur eine einzige Königin, die in einer bestimmten Zeile, also warum nicht speichern nur die Spalte, wo es platziert wurde, in eine eindimensionale Liste (mit sentinel-Wert, wie
1000
angibt, keine Königin verbracht wurde)? So[1, 3, 0, 2]
wäre eine Lösung des 4-Damen-problem, mit den Königinnen bei(0, 1)
,(1, 3)
,(2, 0)
und(3, 2)
(man könnte diese Tupel mitenumerate
, wenn man Sie brauchte). Dies können Sie vermeiden, diefor
Schleife in der backtracking Schritt, und wahrscheinlich macht die überprüfung, ob ein Platz ist sicher noch einfacher, da brauchen Sie nicht zu suchen, dem Vorstand für die queens.Bearbeiten, um auf Ihre weiteren Fragen:
Am Punkt Q1, müssen Sie Tiefe Kopie der platine, weil sonst wirst du am Ende mit einer Liste von Referenzen auf das gleiche board. Vergleichen:
Als für Q2, müssen Sie
return
stoppen Sie den code aus laufen weiter. Könnten Sie trennen diereturn
Anweisung aus dem rekursiven Aufruf, wenn Sie wollen, um es mehr deutlich, dass der return-Wert ist nicht signifikant:Beachten Sie, dass Sie Ihre aktuelle code kann funktionieren, aber Sie tut einige Dubiose Sachen. Sie fordern
is_safe
mitrow
Werte, die außerhalb der Grenzen liegen (row == len(board)
), und es ist nur, weil Ihris_safe
Umsetzung berichtet Sie als unsicher (ohne Absturz), dass Sie backtrack angemessen. Und wenn Sie in der backtracking-code bei Zeile 0 (ganz am Ende der Ausführung), die Sie nur beenden, richtig, weil derfor
Schleife finde keine1
Werte inboard[-1]
(also der letzten Zeile). Ich schlage vor, refactoring Ihres Codes ein bisschen mehr zu vermeiden, sich auf solche Macken für den korrekten Betrieb.deepcopy()
. Wissen Sie, warum das so ist?copy.deepcopy
ist wahrscheinlich der beste Weg, es zu tun. Für einfache Objekte wie (nicht verschachtelte) Listen aus, die Sie kopieren können, andere Möglichkeiten (wielist(old_lst)
oderold_lst[:]
), aber das wird nicht genug sein für eine verschachtelte Struktur wieboard
.Nutzen Sie die power von Python! Ich schlage vor, zu
yield
Ihre Lösungen gefunden, stattreturn
ing Sie. Dieser Weg, den Sie erstellt haben, eine generator für alle Lösungen. Eine Liste aus dieser ist ziemlich straight nach vorne, dann (falls Sie es wirklich benötigen), indem Sie die notationoder einfach
EDIT:
In Ihrem Fall
solve()
undplace_queen()
gemacht werden muss-Generatoren. Insolve()
Sie tun sollten, als Letzte Sache:return place_queen(board, 0, 0)
. Sie zurück einen generator. (Sie können auchfor solution in place_queen(board, 0, 0): yield solution
aber das wäre nur die relais ergaben Werte.)In
place_queen()
statt der Rendite wiereturn place_queen(board, row+1, 0)
sollten Sie Dinge tun, wiefor solution in place_queen(board, row+1, 0): yield solution
.EDIT2:
return
mityield
Ergebnisse in einemplace_queen generator object
während ich erwartete, ich würde ein generator vonlist
statt (was macht Ihre Lösung funktioniert). Vielleicht gibt es etwas, das ich nicht verstehe, über Generatoren. Haben Sie eine Idee?yield
etwas, dann sind Sie ein generator ist, und nicht eine bloße Funktion. Wer verwendet Sie sollte dann Durchlaufen Sie, und nicht nur erwarten, dass man einen Wert. Stattsolution = solve(4)
sollte man tun, sth wiefor solution in solve(4): print solution
oder ähnliches.queens
imis_safe()
), sonst hätte ich angenommen mein Ansatz schnell zu Ihrem code.return
- Anweisung in jeder Schleife innerhalbplace_queen()
,row
wird immer größer und Sie erhalten eine index out of range Fehler.