Python 如何用改进的DFS算法遍历循环有向图
概述 我试图找出如何使用某种DFS迭代算法遍历有向循环图。下面是我目前实现的一个小型mcve版本(它不涉及周期):Python 如何用改进的DFS算法遍历循环有向图,python,algorithm,python-2.7,depth-first-search,demoscene,Python,Algorithm,Python 2.7,Depth First Search,Demoscene,概述 我试图找出如何使用某种DFS迭代算法遍历有向循环图。下面是我目前实现的一个小型mcve版本(它不涉及周期): 类节点(对象): 定义初始化(self,name): self.name=名称 def启动(自): 打印“{}\u start.”格式(自) def中级(自我): 打印“{}\u middle”。格式(自) def端(自身): 打印“{}\u end.”格式(自) 定义(自我): 返回“{0}”。格式(self.name) 类节点重复(节点): 定义初始化(self、name、nu
类节点(对象):
定义初始化(self,name):
self.name=名称
def启动(自):
打印“{}\u start.”格式(自)
def中级(自我):
打印“{}\u middle”。格式(自)
def端(自身):
打印“{}\u end.”格式(自)
定义(自我):
返回“{0}”。格式(self.name)
类节点重复(节点):
定义初始化(self、name、num\u repeats=1):
超级(NodeRepeat,self)。\uuuu init\uuuu(名称)
self.num\u repeats=num\u repeats
def dfs(图表,开始):
“”“使用带有反向子节点的DFS从开始节点遍历图形”“”
访问={}
堆栈=[(开始“”)]
堆栈时:
#要转换dfs->bfs
#a)将堆栈重命名为队列
#b)pop变为pop(0)
节点,父节点=stack.pop()
如果父项为“无”:
如果访问[节点]<3:
node.end()
已访问[节点]=3
未访问的elif节点:
如果已访问。获取(父项)=2:
parent.middle()
elif已访问。获取(父项)==1:
到访[家长]=2
node.start()
已访问[节点]=1
stack.append((节点,无))
#也许你想要一个不同的顺序,如果是这样的话,不要使用相反的顺序
childs=reversed(graph.get(节点,[]))
对于儿童中的儿童:
如果未探访儿童:
stack.append((子节点、节点))
如果名称=“\uuuuu main\uuuuuuuu”:
Sequence1=节点('Sequence1')
MtxPushPop1=节点('MtxPushPop1')
Rotate1=节点('Rotate1')
Repeat1=NodeRepeat('Repeat1',num_repeats=2)
Sequence2=节点('Sequence2')
MtxPushPop2=节点('MtxPushPop2')
Translate=节点('Translate')
Rotate2=节点('Rotate2')
Rotate3=节点('Rotate3')
比例=节点(“比例”)
Repeat2=NodeRepeat('Repeat2',num_repeats=3)
网格=节点(“网格”)
循环图={
Sequence1:[MtxPushPop1,Rotate1],
MTXPUSHPP1:[Sequence2],
旋转1:[重复1],
Sequence2:[MTXPUSHPP2,翻译],
Repeat1:[Sequence1],
MtxPushPop2:[Rotate2],
翻译:[旋转3],
旋转2:[比例],
旋转3:[重复2],
比例:[网格],
重复2:[序列2]
}
dfs(循环图,序列1)
打印'-'*80
a=节点('a')
b=节点('b')
dfs({
a:[b],
b:[a]
},a)
上面的代码正在测试两种情况,第一种是下图的某种表示:
第二个是一个图包含一个“无限”循环{a->b,b->a}
要求
- 不会存在“无限循环”这样的东西,比如说,当发现一个“无限循环”时,会有一个最大阈值(全局变量)来指示何时停止围绕这些“伪无限循环”循环
- 所有图形节点都能够创建循环,但将存在一个名为
的特殊节点,您可以在其中指示循环的迭代次数Repeat
- 上面我发布的mcve是遍历算法的迭代版本,不知道如何处理循环图。理想情况下,解决方案也是迭代的,但如果存在更好的递归解决方案,那就太好了
- 我们在这里讨论的数据结构不应该被称为“有向无环图”,因为在这种情况下,每个节点都有其子节点的顺序,而在图中节点连接没有顺序李>
- 任何东西都可以连接到编辑器中的任何东西。您将能够执行任何块组合,唯一的限制是执行计数器,如果您进行了Neverening循环或太多迭代,它将溢出
- 与上面的代码片段类似,该算法将保留start/middle/after节点的方法执行
# If you don't want global variables, remove the indentation procedures
indent = -1
MAX_THRESHOLD = 10
INF = 1 << 63
def whitespace():
global indent
return '| ' * (indent)
class Node:
def __init__(self, name, num_repeats=INF):
self.name = name
self.num_repeats = num_repeats
def start(self):
global indent
if self.name.find('Sequence') != -1:
print whitespace()
indent += 1
print whitespace() + '%s_start' % self.name
def middle(self):
print whitespace() + '%s_middle' % self.name
def end(self):
global indent
print whitespace() + '%s_end' % self.name
if self.name.find('Sequence') != -1:
indent -= 1
print whitespace()
def dfs(graph, start):
visits = {}
frontier = [] # The stack that keeps track of nodes to visit
# Whenever we "visit" a node, increase its visit count
frontier.append((start, start.num_repeats))
visits[start] = visits.get(start, 0) + 1
while frontier:
# parent_repeat_count usually contains vertex.repeat_count
# But, it may contain a higher value if a repeat node is its ancestor
vertex, parent_repeat_count = frontier.pop()
# Special case which signifies the end
if parent_repeat_count == -1:
vertex.end()
# We're done with this vertex, clear visits so that
# if any other node calls us, we're still able to be called
visits[vertex] = 0
continue
# Special case which signifies the middle
if parent_repeat_count == -2:
vertex.middle()
continue
# Send the start message
vertex.start()
# Add the node's end state to the stack first
# So that it is executed last
frontier.append((vertex, -1))
# No more children, continue
# Because of the above line, the end method will
# still be executed
if vertex not in graph:
continue
## Uncomment the following line if you want to go left to right neighbor
#### graph[vertex].reverse()
for i, neighbor in enumerate(graph[vertex]):
# The repeat count should propagate amongst neighbors
# That is if the parent had a higher repeat count, use that instead
repeat_count = max(1, parent_repeat_count)
if neighbor.num_repeats != INF:
repeat_count = neighbor.num_repeats
# We've gone through at least one neighbor node
# Append this vertex's middle state to the stack
if i >= 1:
frontier.append((vertex, -2))
# If we've not visited the neighbor more times than we have to, visit it
if visits.get(neighbor, 0) < MAX_THRESHOLD and visits.get(neighbor, 0) < repeat_count:
frontier.append((neighbor, repeat_count))
visits[neighbor] = visits.get(neighbor, 0) + 1
def dfs_rec(graph, node, parent_repeat_count=INF, visits={}):
visits[node] = visits.get(node, 0) + 1
node.start()
if node not in graph:
node.end()
return
for i, neighbor in enumerate(graph[node][::-1]):
repeat_count = max(1, parent_repeat_count)
if neighbor.num_repeats != INF:
repeat_count = neighbor.num_repeats
if i >= 1:
node.middle()
if visits.get(neighbor, 0) < MAX_THRESHOLD and visits.get(neighbor, 0) < repeat_count:
dfs_rec(graph, neighbor, repeat_count, visits)
node.end()
visits[node] = 0
Sequence1 = Node('Sequence1')
MtxPushPop1 = Node('MtxPushPop1')
Rotate1 = Node('Rotate1')
Repeat1 = Node('Repeat1', 2)
Sequence2 = Node('Sequence2')
MtxPushPop2 = Node('MtxPushPop2')
Translate = Node('Translate')
Rotate2 = Node('Rotate2')
Rotate3 = Node('Rotate3')
Scale = Node('Scale')
Repeat2 = Node('Repeat2', 3)
Mesh = Node('Mesh')
cyclic_graph = {
Sequence1: [MtxPushPop1, Rotate1],
MtxPushPop1: [Sequence2],
Rotate1: [Repeat1],
Sequence2: [MtxPushPop2, Translate],
Repeat1: [Sequence1],
MtxPushPop2: [Rotate2],
Translate: [Rotate3],
Rotate2: [Scale],
Rotate3: [Repeat2],
Scale: [Mesh],
Repeat2: [Sequence2]
}
dfs(cyclic_graph, Sequence1)
print '-'*40
dfs_rec(cyclic_graph, Sequence1)
print '-'*40
dfs({Sequence1: [Translate], Translate: [Sequence1]}, Sequence1)
print '-'*40
dfs_rec({Sequence1: [Translate], Translate: [Sequence1]}, Sequence1)
#如果不需要全局变量,请删除缩进过程
缩进=-1
最大阈值=10
INF=1=1:
node.middle()
如果访问.get(邻居,0)# If you don't want global variables, remove the indentation procedures
indent = -1
MAX_THRESHOLD = 10
INF = 1 << 63
def whitespace():
global indent
return '| ' * (indent)
class Node:
def __init__(self, name, num_repeats=INF):
self.name = name
self.num_repeats = num_repeats
def start(self):
global indent
if self.name.find('Sequence') != -1:
print whitespace()
indent += 1
print whitespace() + '%s_start' % self.name
def middle(self):
print whitespace() + '%s_middle' % self.name
def end(self):
global indent
print whitespace() + '%s_end' % self.name
if self.name.find('Sequence') != -1:
indent -= 1
print whitespace()
def dfs(graph, start):
visits = {}
frontier = [] # The stack that keeps track of nodes to visit
# Whenever we "visit" a node, increase its visit count
frontier.append((start, start.num_repeats))
visits[start] = visits.get(start, 0) + 1
while frontier:
# parent_repeat_count usually contains vertex.repeat_count
# But, it may contain a higher value if a repeat node is its ancestor
vertex, parent_repeat_count = frontier.pop()
# Special case which signifies the end
if parent_repeat_count == -1:
vertex.end()
# We're done with this vertex, clear visits so that
# if any other node calls us, we're still able to be called
visits[vertex] = 0
continue
# Special case which signifies the middle
if parent_repeat_count == -2:
vertex.middle()
continue
# Send the start message
vertex.start()
# Add the node's end state to the stack first
# So that it is executed last
frontier.append((vertex, -1))
# No more children, continue
# Because of the above line, the end method will
# still be executed
if vertex not in graph:
continue
## Uncomment the following line if you want to go left to right neighbor
#### graph[vertex].reverse()
for i, neighbor in enumerate(graph[vertex]):
# The repeat count should propagate amongst neighbors
# That is if the parent had a higher repeat count, use that instead
repeat_count = max(1, parent_repeat_count)
if neighbor.num_repeats != INF:
repeat_count = neighbor.num_repeats
# We've gone through at least one neighbor node
# Append this vertex's middle state to the stack
if i >= 1:
frontier.append((vertex, -2))
# If we've not visited the neighbor more times than we have to, visit it
if visits.get(neighbor, 0) < MAX_THRESHOLD and visits.get(neighbor, 0) < repeat_count:
frontier.append((neighbor, repeat_count))
visits[neighbor] = visits.get(neighbor, 0) + 1
def dfs_rec(graph, node, parent_repeat_count=INF, visits={}):
visits[node] = visits.get(node, 0) + 1
node.start()
if node not in graph:
node.end()
return
for i, neighbor in enumerate(graph[node][::-1]):
repeat_count = max(1, parent_repeat_count)
if neighbor.num_repeats != INF:
repeat_count = neighbor.num_repeats
if i >= 1:
node.middle()
if visits.get(neighbor, 0) < MAX_THRESHOLD and visits.get(neighbor, 0) < repeat_count:
dfs_rec(graph, neighbor, repeat_count, visits)
node.end()
visits[node] = 0
Sequence1 = Node('Sequence1')
MtxPushPop1 = Node('MtxPushPop1')
Rotate1 = Node('Rotate1')
Repeat1 = Node('Repeat1', 2)
Sequence2 = Node('Sequence2')
MtxPushPop2 = Node('MtxPushPop2')
Translate = Node('Translate')
Rotate2 = Node('Rotate2')
Rotate3 = Node('Rotate3')
Scale = Node('Scale')
Repeat2 = Node('Repeat2', 3)
Mesh = Node('Mesh')
cyclic_graph = {
Sequence1: [MtxPushPop1, Rotate1],
MtxPushPop1: [Sequence2],
Rotate1: [Repeat1],
Sequence2: [MtxPushPop2, Translate],
Repeat1: [Sequence1],
MtxPushPop2: [Rotate2],
Translate: [Rotate3],
Rotate2: [Scale],
Rotate3: [Repeat2],
Scale: [Mesh],
Repeat2: [Sequence2]
}
dfs(cyclic_graph, Sequence1)
print '-'*40
dfs_rec(cyclic_graph, Sequence1)
print '-'*40
dfs({Sequence1: [Translate], Translate: [Sequence1]}, Sequence1)
print '-'*40
dfs_rec({Sequence1: [Translate], Translate: [Sequence1]}, Sequence1)
def dfs_rec(graph, node, parent_repeat_count=INF, visits={}):
visits[node] = visits.get(node, 0) + 1
node.start()
if node not in graph:
node.end()
return
for i, neighbor in enumerate(graph[node][::-1]):
repeat_count = max(1, parent_repeat_count)
if neighbor.num_repeats != INF:
repeat_count = neighbor.num_repeats
if i >= 1:
node.middle()
if visits.get(neighbor, 0) < MAX_THRESHOLD and visits.get(neighbor, 0) < repeat_count:
dfs_rec(graph, neighbor, repeat_count, visits)
node.end()
visits[node] = 0
def traverse(graph,node,process):
seen={node}
current_level=[node]
while current_level:
next_level=[]
for node in current_level:
process(node)
for child in (link for link in graph.get(node,[]) if link not in seen):
next_level.append(child)
seen.add(child)
current_level=next_level
In [24]: traverse(cyclic_graph,Sequence1,process)
Sequence1
MtxPushPop1
Rotate1
Sequence2
Repeat1
MtxPushPop2
Translate
Rotate2
Rotate3
Scale
Repeat2
Mesh