OVERVIEW
我试图弄清楚如何使用某种DFS迭代算法遍历 directed cyclic graphs . 这里's a little mcve version of what I currently got implemented (it doesn'吨处理周期):
class Node(object):
def __init__(self, name):
self.name = name
def start(self):
print '{}_start'.format(self)
def middle(self):
print '{}_middle'.format(self)
def end(self):
print '{}_end'.format(self)
def __str__(self):
return "{0}".format(self.name)
class NodeRepeat(Node):
def __init__(self, name, num_repeats=1):
super(NodeRepeat, self).__init__(name)
self.num_repeats = num_repeats
def dfs(graph, start):
"""Traverse graph from start node using DFS with reversed childs"""
visited = {}
stack = [(start, "")]
while stack:
# To convert dfs -> bfs
# a) rename stack to queue
# b) pop becomes pop(0)
node, parent = stack.pop()
if parent is None:
if visited[node] < 3:
node.end()
visited[node] = 3
elif node not in visited:
if visited.get(parent) == 2:
parent.middle()
elif visited.get(parent) == 1:
visited[parent] = 2
node.start()
visited[node] = 1
stack.append((node, None))
# Maybe you want a different order, if it's so, don't use reversed
childs = reversed(graph.get(node, []))
for child in childs:
if child not in visited:
stack.append((child, node))
if __name__ == "__main__":
Sequence1 = Node('Sequence1')
MtxPushPop1 = Node('MtxPushPop1')
Rotate1 = Node('Rotate1')
Repeat1 = NodeRepeat('Repeat1', num_repeats=2)
Sequence2 = Node('Sequence2')
MtxPushPop2 = Node('MtxPushPop2')
Translate = Node('Translate')
Rotate2 = Node('Rotate2')
Rotate3 = Node('Rotate3')
Scale = Node('Scale')
Repeat2 = NodeRepeat('Repeat2', num_repeats=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 '-'*80
a = Node('a')
b = Node('b')
dfs({
a : [b],
b : [a]
}, a)
上面的代码测试了几个案例,第一个是下图的某种表示:
第二个是最简单的一个图形包含一个"infinite"循环 {a->b, b->a}
REQUIREMENTS
-
当找到一个"infinite cycle"时,有人说't exist such a thing like 2678232 , let' s会有一个最大阈值(全局变量)来指示何时停止循环"pseudo-infinite cycles"
-
所有图形节点都能够创建循环但是会存在一个名为
Repeat
的特殊节点,您可以在其中指示循环循环的循环次数 -
我发布的上述mcve是遍历算法的迭代版本 doesn't know how to deal with cyclic graphs . 理想情况下,解决方案也是迭代的,但如果存在更好的递归解决方案,那就太好了
-
数据结构我们被称为"directed acyclic graphs",因为在这种情况下, each node has its children ordered ,并且在图中节点连接没有顺序 .
-
一切都可以连接到编辑器中的任何内容 . 您将能够执行任何块组合,唯一的限制是执行计数器,如果您进行了无限循环或过多的迭代,它将会溢出 .
-
算法将保留开始/中间/后节点的方法执行,类似于上面的代码片段
QUESTION
任何人都可以提供某种知道如何遍历无限/有限循环的解决方案吗?
REFERENCES
如果此时问题尚不清楚,你可以在这个问题上阅读更多关于这个问题,整个想法将使用遍历算法来实现类似于该文章中所示的类似工具 .
这是一个屏幕截图,显示了这种数据结构的全部功能我想弄清楚如何遍历和运行:
2 回答
在我开始之前,我也希望这些评论能够详细阐述我的成就 . 如果您需要更多解释,请查看我对代码下面的递归方法的解释 .
输入和(格式正确和缩进)输出可以找到here . 如果你想看看我如何格式化输出,请参考代码,也可以是found on CodeSkulptor .
对,解释 . 更容易理解但更低效的递归解决方案,我将用它来帮助解释,如下:
我们要做的第一件事是访问节点 . 我们通过增加字典中节点的访问次数来完成此操作 .
然后我们引发节点的
start
事件 .我们做一个简单的检查,看看节点是否是无子(叶)节点 . 如果是,我们举起
end
事件并返回 .现在我们已经确定节点有邻居,我们遍历每个邻居 . Side Note: 我在递归版本中反转邻居列表(通过使用
graph[node][::-1]
)以维持邻居遍历的相同顺序(从右到左),如在迭代版本中那样 .对于每个邻居,我们首先计算重复计数 . 重复计数从祖先节点传播(继承),因此除非邻居包含重复计数值,否则将使用继承的重复计数 .
如果正在处理第二个(或更高)邻居,则引发当前节点的
middle
事件( not 邻居) .如果可以访问邻居,则访问邻居 . 可访问性检查是通过检查邻居是否被访问的次数小于a)
MAX_THRESHOLD
次(对于伪无限循环)和b)上述计算的重复计数次数来完成的 .我们现在已经完成了这个节点;提升
end
事件并清除其在哈希表中的访问 . 这样做是为了如果某个其他节点再次调用它,则它不会使可访问性检查失败和/或执行少于所需的次数 .根据comment66244567 - 通过忽略到访问节点的链接并执行广度优先搜索将图形缩减为树,因为这将生成更自然(并且可能更 balancer )的树:
使用您的图表和
def process(node): print node
,这会产生:另一个BFS算法iterative deepening DFS(以速度为代价使用更少的内存)在这种情况下不会赢得任何东西:因为你必须存储对被访问的引用节点,你已经消耗了O(n)内存 . 你不需要产生中间结果(但无论如何你都可以 - 例如
yield
处理一个级别后的东西) .