Bfs修改以在有向图中查找兼容路径

时间:2018-03-26 12:20:07

标签: python algorithm graph-theory breadth-first-search

我有以下有向图,每个节点都有一个或多个属性。我尝试修改bfs算法以查找从起始节点到覆盖给定属性的所有可能路径。我也希望我发现的路径不是循环的一部分。 enter image description here

对于此图,如果我从节点1开始并且我想覆盖attr 4,我的算法将找到的路径是:

1-2-3
1-2-5-3
1-2-5-6-8

如果我添加边3-1,那么路径1-2-31-2-5-3我不想被接受,因为它是循环的一部分。所以在我的算法中,我尝试检查上次访问的节点的邻居,如果邻居已经访问过,那么我尝试丢弃此路径,但我的算法在这种情况下不起作用。如果我添加边3-1,它将返回相同的路径。我怎样才能解决这个问题? 这是我的代码:

G = nx.DiGraph()

G.add_edge(1,2)
G.add_edge(2,3)
G.add_edge(2,5)
G.add_edge(3,4)
G.add_edge(5,3)
G.add_edge(5,6)
G.add_edge(5,7)
G.add_edge(6,8)
G.add_edge(3,1)

def checkIfRequiredAttrsAreCovered(path, attrsToBeCovered):
    coveredAttrs = []
    counter = 0
    for node in path:
        coveredAttrs.extend(G.node[node]['attrs'])
    for i in attrsToBeCovered:
        if i in coveredAttrs:
            counter = counter + 1
    if counter == len(attrsToBeCovered):
        return True
    else:
        return False


def bfs(G, startingNode, attrsToBeCovered):
    paths = []
    q = queue.Queue()
    q.put([startingNode])
    while not q.empty():
        v = q.get()
        if checkIfRequiredAttrsAreCovered(v, attrsToBeCovered) == True:
            for i in G.neighbors(v[-1]):
                if i in v:
                    break
            paths.append(v) #print(v)
        else:
            for node in G.neighbors(v[-1]):
                if node not in v:
                    path = []
                    path.extend(v)
                    path.append(node)
                    q.put(path)

    print(paths)

1 个答案:

答案 0 :(得分:1)

我假设您不关心节点是否是更大周期的一部分。例如。如果4连接到1,则3连接到1-2-3-4。如果要处理此问题,可以从每个匹配节点启动dfs,并将当前路径设置为已访问。

首先,you should use snake case in Python

其次,您应该使用set来比较要涵盖的属性所涵盖的属性。对于路径,计算覆盖属性集并比较集:

def check_if_required_attrs_are_covered(G, path, attrs_to_be_covered): # be sure to pass G as an argument here
    covered_attrs = set([G.node[n]['attrs'] for n in path])
    return covered_attrs >= attrs_to_be_covered

第三,对bfs函数的一些评论:

  • 测试if b == True:相当于if b:,因为对于布尔值b == (b == True)(尝试使用True和False来说服自己)
  • 您追加q路径的方式可缩短为q.put(v+ [node])
  • 您可能不需要同步queue:使用列表
  • 使用return而不是print,甚至更好,创建一个在找到路径时生成路径的生成器。
四:问题是什么?查看for i in G.neighbors(v[-1]):循环。 无论您是否break,都可以转到paths.append(v)行。 这就是为什么你不排除带循环的路径。您希望区分循环的正常结束和中断。 这是Python中机密循环语法的完美案例:for...else loop。 我引用了文档:“当没有break发生时,循环的else子句运行”。这给出了以下代码:

for i in G.neighbors(v[-1]):
    if i in v:
        break
else: # no neighbor from v[-1] in v
    yield v # instead of paths.append(v)

但您也可以使用any获得更自然的表达方式:

if not any(i in v for i in G.neighbors(v[-1])):
    yield v # instead of paths.append(v)

这给出了以下代码:

def bfs(G, starting_node, attrs_to_be_covered):
    q = [[starting_node]]
    while q:
        v = q.pop()
        if check_if_required_attrs_are_covered(G, v, attrs_to_be_covered): # be sure to pass G as an argument
            if not any(i in v for i in G.neighbors(v[-1])):
                yield v
        else:
            for node in G.neighbors(v[-1]):
                if node not in v:
                    q.append(v+ [node])

尝试使用:

print (list(bfs(G, 1,  set(["attr4"]))))