簡體   English   中英

Networkx:在二分圖中查找僅由一組中的節點組成的所有最小切割

[英]Networkx: Find all minimal cuts consisting of only nodes from one set in a bipartite graph

在networkx python包中,有沒有辦法找到最小尺寸的所有節點剪切,只包含二分圖中一組的節點? 例如,如果二分圖的兩邊是A和B,我怎樣才能找到完全來自集合B的節點組成的所有最小節點切割? 以下代碼我的工作,但它非常慢:

def get_one_sided_cuts(G, A, B):
    #get all cuts that consist of nodes exclusively from B which disconnect
    #nodes from A
    one_sided_cuts = []
    seen = []

    l = list(combinations(A, 2))

    for x in l:
        s = x[0]
        t = x[1]

        cut = connectivity.minimum_st_node_cut(G, s, t)
        if set(cut).issubset(B) and (cut not in seen):
            one_sided_cuts.append(cut)
        seen.append(cut)

    #find minimum cut size
    cur_min = float("inf")
    for i in one_sided_cuts:
        if len(i) < cur_min:
            cur_min = len(i)

    one_sided_cuts = [x for x in one_sided_cuts if len(x) == cur_min]

    return one_sided_cuts

請注意,這實際上只檢查是否存在最小切割,如果移除,將僅斷開A中的兩個節點。 如果您的解決方案執行此操作(而不是找到將任意兩個節點分開的剪切),那也沒關系。 關於如何更有效地做到這一點的任何想法?

正如評論中所述,對“最小尺寸的所有節點切割僅包含來自二部圖中的一個集合的節點”有幾種解釋。 它或者意味着

  1. 當將切割限制在一組二分圖中時,所有節點最小尺寸切割,或
  2. 所有節點都在不受約束的意義上(由A或B的節點組成)完全位於B中。

從您感興趣的代碼示例2.根據文檔,有一種方法可以加快計算速度,並從配置文件結果中獲得一些幫助。 每個圖形都有輔助結構,用於確定最小節點切割。 根據http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf中的算法9,每個節點被2個節點替換,添加額外的有向邊等我們可以重用這些結構而不是重建他們在一個緊密的循環中:

案例2的改進:

from networkx.algorithms.connectivity import (
    build_auxiliary_node_connectivity)
from networkx.algorithms.flow import build_residual_network

from networkx.algorithms.flow import edmonds_karp

def getone_sided_cuts_Case2(G, A, B):
    # build auxiliary networks
    H = build_auxiliary_node_connectivity(G)
    R = build_residual_network(H, 'capacity')


    # get all cutes that consist of nodes exclusively from B which disconnet
    # nodes from A
    one_sided_cuts = []
    seen           = []

    l = list(combinations(A,2))

    for x in l:
        s = x[0]
        t = x[1]

    cut = minimum_st_node_cut(G, s, t, auxiliary=H, residual=R)
    if set(cut).issubset(B):
        if cut not in seen:
            one_sided_cuts.append(cut)
    seen.append(cut)

    # Find minimum cut size
    cur_min = float('inf')
    for i in one_sided_cuts:
        if len(i) < cur_min:
            curr_min = len(i)

    one_sided_cuts = [x for x in one_sided_cuts if len(x) == cur_min]

    return one_sided_cuts

對於分析目的,您可以使用以下或Networkx中的內置二分圖生成器之一:

def create_bipartite_graph(size_m, size_n, num_edges):
    G = nx.Graph()

    edge_list_0 = list(range(size_m))
    edge_list_1 = list(range(size_m,size_m+size_n))
    all_edges = []

    G.add_nodes_from(edge_list_0, bipartite=0)
    G.add_nodes_from(edge_list_1, bipartite=1)

    all_edges = list(product(edge_list_0, edge_list_1))
    num_all_edges = len(all_edges)

    edges = [all_edges[i] for i in random.sample(range(num_all_edges), num_edges)]
    G.add_edges_from(edges)

    return G, edge_list_0, edge_list_1

使用%timeit ,第二個版本運行速度提高約5-10%。

對於案例1,邏輯更復雜一些。 我們需要考慮僅在B內部節點的最小切割。這需要以下列方式更改minimum_st_node_cut 然后在您的解決方案或上面給出的案例2解決方案rest_minimum_st_node_cut minimum_st_node_cut所有出現替換為rest_minimum_st_node_cut ,注意新函數還需要指定集合AB ,必要:

def rest_build_auxiliary_node_connectivity(G,A,B):
    directed = G.is_directed()

    H = nx.DiGraph()

    for node in A:
        H.add_node('%sA' % node, id=node)
        H.add_node('%sB' % node, id=node)
        H.add_edge('%sA' % node, '%sB' % node, capacity=1)

    for node in B:
        H.add_node('%sA' % node, id=node)
        H.add_node('%sB' % node, id=node)
        H.add_edge('%sA' % node, '%sB' % node, capacity=1)        

    edges = []
    for (source, target) in G.edges():
        edges.append(('%sB' % source, '%sA' % target))
        if not directed:
            edges.append(('%sB' % target, '%sA' % source))
    H.add_edges_from(edges, capacity=1)

    return H

def rest_minimum_st_node_cut(G, A, B, s, t, auxiliary=None, residual=None, flow_func=edmonds_karp):

    if auxiliary is None:
        H = rest_build_auxiliary_node_connectivity(G, A, B)
    else:
        H = auxiliary

    if G.has_edge(s,t) or G.has_edge(t,s):
        return []
    kwargs = dict(flow_func=flow_func, residual=residual, auxiliary=H)

    for node in [x for x in A if x not in [s,t]]:
        edge = ('%sA' % node, '%sB' % node)
        num_in_edges = len(H.in_edges(edge[0]))
        H[edge[0]][edge[1]]['capacity'] = num_in_edges

    edge_cut = minimum_st_edge_cut(H, '%sB' % s, '%sA' % t,**kwargs)

    node_cut = set([n for n in [H.nodes[node]['id'] for edge in edge_cut for node in edge] if n not in A])

    return node_cut - set([s,t])

然后我們有,例如:

In [1]: G = nx.Graph()
        # A = [0,1,2,3], B = [4,5,6,7]
In [2]: G.add_edges_from([(0,4),(0,5),(1,6),(1,7),(4,1),(5,1),(6,3),(7,3)])
In [3]: minimum_st_node_cut(G, 0, 3)
           {1}
In [4]: rest_minimum_st_node_cut(G,A,B,0,3)
           {6, 7}

最后請注意,如果兩個節點相鄰,則minimum_st_edge_cut()函數返回[] 有時,慣例是在這種情況下返回一組n-1節點,除源或接收器之外的所有節點。 無論如何,使用空列表約定,並且由於您對Case 2的原始解決方案在A節點對上循環,您可能會將[]作為大多數配置的返回值,除非A中的節點沒有相鄰,例如。

編輯

OP遇到了二分圖的問題,其中集A,B包含整數和str類型的混合。 在我看來, build_auxiliary_node_connectivity將這些str節點轉換為導致沖突的整數。 我重寫了上面的內容,我認為這樣可以解決它。 我沒有在networkx文檔中看到任何關於此的內容,因此要么使用所有整數節點,要么使用上面的rest_build_auxiliary_node_connectivity()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM