Ваш подход имеет следующие проблемы:
- Алгоритм просто выполняет обход (а не поиск), и когда все узлы были посещены (обнаружены), стек будет развернут, пока не будет извлечен последний квадрат от него. Этот последний квадрат является первым, когда-либо помещенным в стек, поэтому, конечно, он не является узлом, представляющим конец длинного пути.
- В него не входит лог c для обнаружения обхода.
- На основе «обнаруженного» массива вы будете анализировать квадрат только один раз, что означает, что вы ожидаете найти тур без возврата, так как после первого возврата вы не сможете повторно использовать уже посещенные квадраты в некотором варианте пути.
- Массив предшественника - это идея, которая используется при поиске в ширину, но не очень подходит для поиска в глубину
- Вы предполагаете, что есть решение , но вы вызываете функцию с сеткой 5x5, и на закрытых досках не проводится закрытый тур коня
Попытка реализовать это с помощью стека, а не рекурсии, усложняет для вас вещи, чем необходимо .
Я изменил ваш код для использования рекурсии и решил вышеуказанные проблемы.
offsets = (
(-1, -2), (1, -2),
(-2, -1), (2, -1),
(-2, 1), (2, 1),
(-1, 2), (1, 2),
)
# We don't need the board variable. Just the number of rows/cols are needed:
def is_legal_pos(rows, cols, pos):
i, j = pos
return 0 <= i < rows and 0 <= j < cols
def tour(rows, cols, start):
discovered = set()
n = rows * cols
def dfs(current): # Use recursion
discovered.add(current)
for move in offsets:
row_offset, col_offset = move
# Don't name your variable next, as that is the name of a native function
neighbor = (current[0] + row_offset, current[1] + col_offset)
# Detect whether a closed tour was found
if neighbor == start and len(discovered) == n:
return [start, current] # If so, create an extendable path
if is_legal_pos(rows, cols, neighbor) and neighbor not in discovered:
path = dfs(neighbor)
if path: # Extend the reverse path while backtracking
path.append(current)
return path
# The choice of "current" did not yield a solution. Make it available
# for a later choice, and return without a value (None)
discovered.discard(current)
return dfs(start)
# No need for a board variable. Just number of rows/cols is enough.
# As 5x5 has no solution, call the function for a 6x6 board:
print(tour(6, 6, (0, 0)))
Чтобы сделать это с явным стеком, вам также нужно поместить состояние for
l oop в стек, т.е. вы должны как-то знать, когда заканчивается al oop. Для этого вы можете сделать стек таким, чтобы один элемент в нем представлял собой список соседей, которые еще необходимо посетить, включая того, который является «текущим». Таким образом, в стеке будет список списков:
offsets = (
(-1, -2), (1, -2),
(-2, -1), (2, -1),
(-2, 1), (2, 1),
(-1, 2), (1, 2),
)
# We don't need the board variable. Just the number of rows/cols are needed:
def is_legal_pos(rows, cols, pos):
i, j = pos
return 0 <= i < rows and 0 <= j < cols
def tour(rows, cols, start):
discovered = set()
n = rows * cols
stack = [[start]]
while stack:
neighbors = stack[-1]
if not neighbors: # Need to backtrack...
stack.pop()
# remove the node that ended this path, and unmark it
neighbors = stack[-1]
current = neighbors.pop(0)
discovered.discard(current)
continue
while neighbors:
current = neighbors[0]
discovered.add(current)
neighbors = [] # Collect the valid neighbors
for move in offsets:
row_offset, col_offset = move
# Don't name your variable next, as that is the name of a native function
neighbor = (current[0] + row_offset, current[1] + col_offset)
# Detect whether a closed tour was found
if neighbor == start and len(discovered) == n:
path = [start] # If so, create the path from the stack
while stack:
path.append(stack.pop()[0])
return path
if is_legal_pos(rows, cols, neighbor) and neighbor not in discovered:
neighbors.append(neighbor)
# Push the collection of neighbors: deepening the search
stack.append(neighbors)
# No need for a board variable. Just number of rows/cols is enough.
# As 5x5 has no solution, call the function for a 6x6 board:
print(tour(6, 6, (0, 0)))
Лично я нахожу этот код намного более запутанным, чем рекурсивная версия. Вы действительно должны go для рекурсии.
Обратите внимание, что этот наивный подход далеко не эффективен. На самом деле нам немного повезло с доской 6х6. Если бы вы перечислили смещения в другом порядке, есть вероятность, что для поиска решения потребуется гораздо больше времени.
Пожалуйста, ознакомьтесь со статьей Википедии Kinght's Tour , в которой перечислены несколько алгоритмов, которые гораздо эффективнее.