我正在尝试创建一个具有众所周知的滑动拼图的不同可能状态的树
如果你不了解它,那就是:
[3 5 2]
[4 1]
[8 6 7]
你必须这样做:
[1 2 3]
[4 5 6]
[7 8 ]
基本上,每个州都会产生新的状态,具体取决于空白区域的移动方式(向上,向下,向左或向右)
我想要的是创建一个树,其中所有状态都将root作为拼图的初始状态,但是当向树添加子(新状态)时,它应该检查该状态是否已经添加到树的任何位置。树
你介意帮我实现吗?在此先感谢:)
这是我当前的代码,它会抛出RecursionError: maximum recursion depth exceeded while calling a Python object
节点类:
class Node(object):
def __init__(self, value, parent=None):
self.parent = parent
self.value = value
self.children = []
def append(self, obj):
if obj is not self.parent and obj not in self.children and obj is not None:
self.children.append(obj)
def has_children(self):
return len(self.children) > 0
def __iter__(self):
yield self.value
for v in chain(*map(iter, self.children)):
yield v
def find_root(self):
p = self
while p.parent is not None:
p = p.parent
return p
树生成方法(将self
视为拼图状态):
def to_tree(self, parent=None):
values = []
if parent is not None:
for child in parent.find_root():
values.append(child)
value = nd.Node(self, parent)
if value not in values:
children = [move[1].to_tree(value) for move in self.get_possible_movements()]
for child in children:
if child is not None:
value.append(child)
return value
else:
return None
答案 0 :(得分:2)
我会尽力回答你进步的直接障碍:
RecursionError: maximum recursion depth exceeded while calling a Python object
这意味着“活动”函数调用的数量(及其本地状态)超出限制。您可以尝试提高该限制(我已经确定可以在某处配置),但还有另一种更常用的技术来修复它。
在伪代码中,通过树搜索(这似乎就是你正在做的事情)看起来像这样:
find(predicate, node):
if predicate(node):
return node # found it
for child in node.children:
res = find(predicate, child):
if res:
return res # found it
return false # not found
predicate
是一个函数,它返回一个布尔值,指示是否找到搜索到的节点,这会推广此搜索。
问题在于,根据树的高度,这可能会超出递归限制,如您所见。避免此限制的另一种方法是不使用递归。而不是将临时状态存储在堆栈上,为它们构建一个专用容器:
find(predicate, node):
temp = [node]
while not temp.empty():
node = temp.pop()
if predicate(node):
return node # found it
for child in temp.children:
temp.push(child)
return false # not found
现在,重点是调用深度移动到temp
容器。但是,让我们看一下详细信息push
和pop
来电,因为它们并不完全清楚它们的作用。如果你想模仿上面的递归版本,你将不得不使用堆栈(LIFO)。另外,你必须以相反的顺序将孩子们推到堆叠上,但孩子们的顺序可能无关紧要。这意味着在第一次迭代之后,您将拥有容器中给定节点的所有直接子节点。在第二次迭代中,删除并处理一个直接子节点,这将添加该节点的子节点。换句话说,搜索首先进入树的深度,因此称为“深度优先搜索”(DFS)。
这种称为“广度优先搜索”(BFS)的变体。在那里,您使用队列(FIFO)而不是堆栈(LIFO)作为容器。第一次迭代后的状态是相同的,给定节点的所有直接子节点。然而,它会检查这些孩子并将他们的孩子添加到容器中,但只有在检查完所有孩子后才开始检查孙子。
关于这种非递归方法的一句话:如果你把它作为进一步开发的基础,这同时会更灵活一些。例如,如果您有多种方法可以到达同一节点(即,如果它不是树),您可以将已经到达的所有子项存储在第二个容器中以避免循环。另一种方法是根据他们与解决方案的距离来命令孩子,以便不遵循不提供福利的路径。
通常,递归是一种很少使用的工具。理解它确实很重要,特别是数学中的递归定义,但在编码中使用它通常是不切实际的。你会发现那些有不同看法的人,这更像是一种观点,而不是一个可靠的主张,尽管我可以在其背后提供一些经验和成功来支持它。
答案 1 :(得分:1)
除了超过最大递归深度之外,我认为您的实现也可能产生无限循环。由于values
列表的范围已本地化为每个to_tree
调用,因此如果已访问过某个州,则无需查找中心位置。这是一个基于堆栈的迭代的示例,使用拼图状态的位表示,拟合为4 * 9 = 36位整数。例如:
123
456
780
将表示为:
0001 0010 0011
0100 0101 0110
0111 1000 0000
反过来联系在一起:
0| 8| 7| 6| 5| 4| 3| 2| 1
0000 1000 0111 0110 0101 0100 0011 0010 0001
0b000010000111011001010100001100100001
=> 2271560481
initialState()
=> 2271560481
让我们添加一些功能来制作和显示状态:
from sys import stdout
def showState(state):
mask = 15
for i in xrange(9):
if i % 3 == 0 and i > 0:
stdout.write("\n")
stdout.write(str((state & mask) >> 4 * i))
mask = mask << 4
stdout.write("\n")
def makeState(arr):
state = 0
for i in xrange(9):
state = state | (arr[i] << 4 * i)
return state
def initialState():
return makeState([1,2,3,4,5,6,7,8,0])
现在我们需要找到零指数:
def findZero(state):
mask = 15
i = 0
while mask & state:
mask = mask << 4
i = i + 1
return i
将相邻数字移动到单元格为零:
def move(state, fromIdx, toIdx):
x = (state & (15 << 4 * fromIdx)) >> 4 * fromIdx
state = state & (2**36 - 1 ^ (15 << 4 * fromIdx) ^ (15 << 4 * toIdx))
state = state | (x << 4 * toIdx)
return state
def moves(idx):
# 012
# 345
# 678
return [
[1,3], [0,2,4], [1,5],
[0,4,6], [1,3,5,7], [2,4,8],
[3,7], [4,6,8], [5,7]
][idx]
让我们添加您正在使用的Node类的版本:
class Node(object):
def __init__(self, state, parent=None):
self.parent = parent
self.state = state
self.children = []
def append(self, obj):
self.children.append(obj)
设置根和一个全局对象states_to_nodes
,它将访问状态映射到将该状态保存为值的节点:
root = Node(initialState())
states_to_nodes = {initialState(): root}
这是一个基于堆栈的迭代,避免了最大递归深度错误:
stack = [root]
while stack:
node = stack.pop()
zero_index = findZero(node.state)
for i in moves(zero_index):
maybe_new_state = move(node.state, i, zero_index)
if not maybe_new_state in states_to_nodes:
new_node = Node(maybe_new_state)
states_to_nodes[maybe_new_state] = new_node
node.append(new_node)
stack.append(new_node)
else:
node.append(states_to_nodes[maybe_new_state])
输出:
example_state = makeState([5,1,3,8,6,0,2,7,4])
print "Example state:\n"
showState(example_state)
print "\nChildren states:\n"
for child in states_to_nodes[example_state].children:
showState(child.state)
print
"""
Example state:
513
860
274
Children states:
510
863
274
513
806
274
513
864
270
"""