首先,从 pdf 文件显示在图像上的算法不是解决哈密尔顿路径问题的方法,而是解决迷宫生成问题,因为最终路径有几个分支。
要查找迷宫生成算法,请参阅:
https ://en.wikipedia.org/wiki/Maze_generation_algorithm
现在这是一个在 N*M 2D 网格上生成哈密顿路径的简单算法:
设一个 N M 网格为(例如,4 5):
噢噢噢 | | | | 噢噢噢 | | | | 噢噢噢 | | | | 哦哦哦
让我们从东/北角开始,向西和向东创建一个简单的锯齿形:
噢噢噢 噢噢噢 噢噢噢
哦哦哦
现在我们有一条哈密顿路径。
让我们搜索两个粘合的边缘,一个在另一个前面。它们是循环的开始和结束:
噢噢噢
O-OXO-OO | O-OXO-OO |
哦哦哦
确保环内至少有一条边粘在环外的边上,否则转至步骤 3:
噢噢噢
O-OXO-OO | O-OXOxO-O |
OO-OxO-O
快捷循环:
噢噢噢
哦哦哦 | | | OO OxO-O |
OO-OxO-O
通过另外两个胶合边缘重新连接环:
噢噢噢
哦哦哦 | | | 哦哦哦 | | |
哦哦哦
如果哈密顿路径不够随机化,请转到步骤 3。
只有开始和结束不会移动。要随机化结束或开始,您可以用另一种算法替换初始之字形:
- 选择四个角之一
- 搜索所有未访问的邻居
- 如果没有邻居,则填充地图,否则进行步骤4
- 只保留在一侧、左侧或右侧有空白或已访问单元格的邻居(换句话说,沿非访问区域边界行走的邻居)
- 选择其中一个邻居,访问它并转到步骤 2
结果可能如下所示:
O-O-O-O-O
|
O-O-O-O O
| | |
O O-O O O
| | | |
O-O-O O-O
使用这种算法,起点仍然在一个角落,但终点可以在任何地方。要随机化开始和结束,您可以应用一种算法,您可以在开始或结束时根据需要进行多次迭代。让我们开始吧:
- 找到起点:
|
v
哦哦哦
|
哦哦哦
| | |
哦哦哦哦
| | | |
哦哦哦
- 找到一个不直接连接到起点的邻居(您总是会在 2D 网格中找到一个):
哦哦哦
|
->哦哦哦
| | |
哦哦哦哦
| | | |
哦哦哦
- 从一开始(分别从最后)查找您到达它的位置:
哦哦哦
|
OXO-OO O
| | |
哦哦哦哦
| | | |
哦哦哦
- 剪切此链接并在此点和起点之间创建一个链接:
哦哦哦
| |
哦哦哦哦
| | |
哦哦哦哦
| | | |
哦哦哦
开始移动了两个单元格。开始和结束就像在棋盘上一样,它们只能在相同颜色的情况下移动。
现在你的路径是完全随机的。
这是Python中的整个算法。你可以在这里运行它:http:
//www.compileonline.com/execute_python3_online.php
结果存储在一个数组 ( self.gameGrid
) 中,该数组记录了两次(带有箭头以及节点和线)。前两个粘合的边缘称为排列,第二个称为交集。
import random
import enum
class From(enum.Enum):
NOWHERE = 1
NORTH = 2
EAST = 3
SOUTH = 4
WEST = 5
class Hamiltonian:
def __init__(self, width: int, height: int, start: tuple = (0, 0)):
self.arcs = {From.NORTH: (0, -1), From.SOUTH: (0, 1), From.EAST: (1, 0), From.WEST: (-1, 0)}
self.width = width
self.height = height
self.start = start
self.grid = {(i, j): self._zig_zag(i, j) for i in range(width) for j in range(height)}
self.grid[start] = From.NOWHERE
self.curr_loop = []
def generate(self, count: int = 100):
for i in range(count):
sp = self._split_grid()
self._modify_path(sp)
tu = self._mend_grid(sp)
self._modify_path(tu)
def _modify_path(self, spl):
pt_a, pt_b = spl
pta, ptb = self.grid[pt_a], self.grid[pt_b]
orientation = pta
if orientation in [From.NORTH, From.SOUTH]:
if pt_a[0] < pt_b[0]:
pta, ptb = From.EAST, From.WEST
else:
pta, ptb = From.WEST, From.EAST
else:
if pt_a[1] < pt_b[1]:
pta, ptb = From.SOUTH, From.NORTH
else:
pta, ptb = From.NORTH, From.SOUTH
self.grid[pt_a] = pta
self.grid[pt_b] = ptb
def _move(self, pt) -> [tuple, None]:
if pt in self.grid and self.grid[pt] != From.NOWHERE:
(x, y), (dx, dy) = pt, self.arcs[self.grid[pt]]
if (x + dx, y + dy) in self.grid:
return x + dx, y + dy
return None
def _set_loop(self, start, stop):
self.curr_loop = []
point = start
while point and len(self.curr_loop) <= len(self.grid) and point != stop and self.grid[point] != From.NOWHERE:
point = self._move(point)
self.curr_loop.append(point)
return point == stop
def _split_grid(self) -> tuple:
candidates = []
for pt, dx in self.grid.items():
x, y = pt
if dx == From.NORTH:
cx = (x+1, y - 1)
if cx in self.grid and self.grid[cx] == From.SOUTH:
candidates.append((pt, cx))
elif dx == From.SOUTH:
cx = (x+1, y + 1)
if cx in self.grid and self.grid[cx] == From.NORTH:
candidates.append((pt, cx))
elif dx == From.EAST:
cx = (x + 1, y + 1)
if cx in self.grid and self.grid[cx] == From.WEST:
candidates.append((pt, cx))
elif dx == From.WEST:
cx = (x - 1, y + 1)
if cx in self.grid and self.grid[cx] == From.EAST:
candidates.append((pt, cx))
if len(candidates) > 0:
start, end = random.choice(candidates)
if self._set_loop(start, end):
return start, end
elif not self._set_loop(end, start):
raise Exception('Cannot split. Loop failed.')
return end, start
def _mend_grid(self, sp):
candidates = []
for pt, dx in self.grid.items():
(x, y), lx = pt, pt in self.curr_loop
if dx == From.NORTH:
cx = (x+1, y - 1)
rx = cx in self.curr_loop
if cx in self.grid and self.grid[cx] == From.SOUTH and rx != lx:
candidates.append((pt, cx))
elif dx == From.SOUTH:
cx = (x+1, y + 1)
rx = cx in self.curr_loop
if cx in self.grid and self.grid[cx] == From.NORTH and rx != lx:
candidates.append((pt, cx))
elif dx == From.EAST:
cx = (x + 1, y + 1)
rx = cx in self.curr_loop
if cx in self.grid and self.grid[cx] == From.WEST and rx != lx:
candidates.append((pt, cx))
elif dx == From.WEST:
cx = (x - 1, y + 1)
rx = cx in self.curr_loop
if cx in self.grid and self.grid[cx] == From.EAST and rx != lx:
candidates.append((pt, cx))
a, b = sp
if (a, b) in candidates:
candidates.remove((a, b))
elif (b, a) in candidates:
candidates.remove((b, a))
if len(candidates) > 0:
return random.choice(candidates)
else:
return sp
def _zig_zag(self, x: int, y: int) -> From:
even = y % 2 == 0
if (x == 0 and even) or (x == self.width - 1 and not even):
return From.NORTH
return From.WEST if even else From.EAST
def print_path(self):
result_str = ''
for y in range(self.height):
for x in range(self.width):
if (self.grid[x, y] == From.NORTH) or ((y > 0) and (self.grid[x, y - 1] == From.SOUTH)):
result_str = result_str + ' |'
else:
result_str = result_str + ' '
result_str = result_str + ' \n'
for x in range(self.width):
if (self.grid[x, y] == From.WEST) or ((x > 0) and (self.grid[x - 1, y] == From.EAST)):
result_str = result_str + '-O'
else:
result_str = result_str + ' O'
result_str = result_str + ' \n'
print(result_str)
if __name__ == '__main__':
h = Hamiltonian(5, 5)
h.generate(500)
h.print_path()