def max_pairs(V1, V2, func=dfs): """ ford fulkerson algorithm For maximum pairing: :param: @G: graph @V1,V2: groups of vertex for finding mach @func: function to use for find adding way by default is dfs, can be bfs :return: list of vertexes pairs for max pairing (=Original vertices) efficiency: O(|E|*n) where n is max pairing, in worst case n=|V1| """ G_pairs = Graph() dic1, dic2 = {v: Vertex(name=v.name) for v in V1}, {v: Vertex(name=v.name) for v in V2} dic, s, t = {**dic1, **dic2}, Vertex(name='s'), Vertex(name='t') dic = {dic[k]: k for k in dic} for v in dic1: G_pairs.connect(from_=s, to=dic1[v], weight=1) for v in dic2: G_pairs.connect(from_=dic2[v], to=t, weight=1) for v in dic1: for u in v.Adj: if u in dic2: G_pairs.connect(from_=dic1[v], to=dic2[u], weight=1) max_flow_ = ford_fulkerson(G_pairs, s, t, func=func) pairs = [] for e in max_flow_: if max_flow_[e] and e.from_ != s and e.to != t: # pairs.append(dic[e.from_].Adj[dic[e.to]]) pairs.append((dic[e.from_], dic[e.to])) return pairs
def johnson(G): """ johnson algorithm: found distance between all pairs of vertex this method work also with negative edges @params: @G: graph return: (matrix of distances,matrix of pi) the algorithm: 1. add vertex 's' with edges to all G.V with weight=0 2. run bellman_ford from 's' => h[v] is the distance of v from 's' for all V 3. for all w(v,u) from G.E add h[u]-h[v] 4. D, PI = all_pairs_dijkstra(G) 5. Correction of distances and edges: D[v,u]-=(h[u]-h[v]) w(v,u)-= (h[u]-h[v]) for all G.E efficiency: if |E| * 0.75 < |V|^2: => O(V^2*log V) if |E| * 0.75 > |V|^2: => O(V*E) = O(|V|^3) """ s = Vertex(name='s') G.V[s] = None for v in G.V: if v is not s: G.connect(from_=s, to=v, weight=0) pi = bellman_ford(G, s) if not pi: # if there is negative circle in the graph return False h = {v: v.data['key'] for v in G.V} for e in G.E: e.weight += (h[e.from_] - h[e.to]) D, PI = all_pairs_dijkstra(G) for i in range(len(D)): for j in range(len(D[i])): D[i][j] -= (h[list(G.V.keys())[i]] - h[list(G.V.keys())[j]]) for e in list(s.edges.keys()): G.disconnect(e=e) del G.V[s] for e in G.E: e.weight -= (h[e.from_] - h[e.to]) return list(np.array(D)[:-1, :-1]), list(np.array(PI)[:-1, :-1])
def kruskal(G, restore=False): """ kruskal algorithm: @params: @G: graph @restore: if restore the solution return: return pi dictionary if restore=True the mst graph (=Minimal spreadsheet graph) will be return also efficiency: the sort of the edges: O(|E|log(|E|)) , the loop: O(|V|log(|V|) => total: O(|E|log(|E|) if the edges already sorted kruskal is fasted than prim """ def union(e): v, u = set_id[e.from_], set_id[e.to] v, u = (v, u) if sets[v].n > sets[u].n else (u, v) for node in sets[u]: set_id[node.data] = v sets[v] = sets[v] + sets[u] sets.pop(u) E_sor, sets, set_id = sorted( G.E, key=lambda e: e.weight), {v: TwoWayList() for v in G.V}, {v: v for v in G.V} for v, lis in sets.items(): lis += v A = [] for e in E_sor: if set_id[e.from_] is not set_id[e.to]: A.append(e) union(e) if restore: V = {v: Vertex(name=v.name, data=v.data) for v in G.V} G_mst = GraphNotAimed() for e in A: G_mst.connect(from_=V[e.from_], to=V[e.to], weight=e.weight) return A, G_mst return A
def prim(G, restore=False): """ prim algorithm: if |E| * 0.75 < |V|^2 : the struct will be min heap else: the struct will be array @params: @G: graph @restore: if restore the solution return: return pi dict if restore=True the mst graph (=Minimal spreadsheet graph) will be return also efficiency: if |E| * 0.75 < |V|^2: => O(Vlog V) if |E| * 0.75 > |V|^2: => O(E) = O(|V|^2) """ struct = 'heap' if len(G.E) < (len(G.V)**2) * 0.75 else 'array' pi = {v: None for v in G.V} for v in G.V: v.data = {'key': float('inf'), 'in heap': True} list(G.V.keys())[0].data['key'] = 0 key = lambda v: v.data['key'] Q = BinaryHeapPrim(arr=G.V, compare=min, key=key) if struct == 'heap' else [v for v in G.V] while struct == 'heap' and Q.arr or struct == 'array' and Q: v, v.data['in heap'] = Q.extract() if struct == 'heap' else Q.pop( Q.index(min(Q, key=key))), False for e in v.edges: if e.to.data['in heap'] and e.weight < e.to.data['key']: pi[e.to], e.to.data['key'] = v, e.weight if struct == 'heap': Q.add(Q.pop(e.to.data['i'])) if restore: V = {v: Vertex(name=v.name, data=v.data) for v in G.V} G_mst = GraphNotAimed() for v, u in pi.items(): if u: G_mst.connect(from_=V[v], to=V[u], weight=v.data['key']) return pi, G_mst return pi
def init_G_r(G): """ init the Residual graph @params: @G: graph efficiency: O(|E|+|V|) """ G_r = Graph() V = [Vertex(name=v.name) for v in G.V] dic = {} for v1, v2 in zip(G.V, V): dic[v1] = v2 for e in G.E: G_r.connect(from_=dic[e.from_], to=dic[e.to], weight=e.weight) return G_r, dic
def tie_well_graph(G): """ efficiency: O(V+E) """ tie_well = sccg(G) G_scc = Graph() for v_group, i in zip(tie_well, range(len(tie_well))): v = Vertex(name=str(v_group), data={'group': v_group}) for v1 in v_group: v1.data['group'] = v for v in G.V: for u in v.Adj: if v.data['group'] != u.data['group']: G_scc.connect(from_=v.data['group'], to=u.data['group'], weight=v.Adj[u].weight) return G_scc
for v in dic1: for u in v.Adj: if u in dic2: G_pairs.connect(from_=dic1[v], to=dic2[u], weight=1) max_flow_ = ford_fulkerson(G_pairs, s, t, func=func) pairs = [] for e in max_flow_: if max_flow_[e] and e.from_ != s and e.to != t: # pairs.append(dic[e.from_].Adj[dic[e.to]]) pairs.append((dic[e.from_], dic[e.to])) return pairs if __name__ == '__main__': V = [Vertex(name=str(i)) for i in range(8)] V[0].name, V[1].name, V[2].name, V[3].name, V[4].name, V[5].name, V[6].name, V[7].name = \ 's', '1', '2', '3', '4', '5', '6', 't' G = Graph() for i in range(8): G.V[V[i]] = None G.connect(from_=V[0], to=V[1], weight=3) G.connect(from_=V[0], to=V[2], weight=3) G.connect(from_=V[1], to=V[2], weight=1) G.connect(from_=V[1], to=V[3], weight=3) G.connect(from_=V[2], to=V[3], weight=1) G.connect(from_=V[2], to=V[4], weight=3) G.connect(from_=V[3], to=V[4], weight=2) G.connect(from_=V[3], to=V[5], weight=3) G.connect(from_=V[4], to=V[5], weight=2) G.connect(from_=V[4], to=V[6], weight=3)
D, PI = all_pairs_dijkstra(G) for i in range(len(D)): for j in range(len(D[i])): D[i][j] -= (h[list(G.V.keys())[i]] - h[list(G.V.keys())[j]]) for e in list(s.edges.keys()): G.disconnect(e=e) del G.V[s] for e in G.E: e.weight -= (h[e.from_] - h[e.to]) return list(np.array(D)[:-1, :-1]), list(np.array(PI)[:-1, :-1]) if __name__ == '__main__': V = [Vertex(name=str(i)) for i in range(8)] # V[0].name, V[1].name, V[2].name, V[3].name, V[4].name, V[5].name, V[6].name = '0', '1', '2', '3', '4', '5', '6' # r = Edge(from_=V[0], to=V[1], weight=3) G = GraphNotAimed() for i in range(8): G.V[V[i]] = None G.connect(from_=V[0], to=V[1], weight=4) G.connect(from_=V[0], to=V[4], weight=3) G.connect(from_=V[0], to=V[3], weight=2) G.connect(from_=V[1], to=V[2], weight=5) G.connect(from_=V[1], to=V[4], weight=4) G.connect(from_=V[1], to=V[5], weight=6) G.connect(from_=V[2], to=V[5], weight=1) G.connect(from_=V[3], to=V[4], weight=6) G.connect(from_=V[4], to=V[5], weight=8) # ---------------------