You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取同一二分图的多个不同最大匹配

Great question! Generating hundreds of distinct maximum matchings in a large bipartite graph is totally doable—here's how to approach it, building on the Hopcroft-Karp algorithm you're already using:

Core Approach: Alternating Cycle Swaps

The key insight is that maximum matchings aren't unique, and we can generate new ones by swapping edges in alternating cycles within the graph. An alternating cycle is a cycle where edges flip between being part of the current matching and not part of it. Swapping which edges are matched in these cycles preserves the total size of the matching (so it stays maximum).

Here's a step-by-step breakdown:

  • Start with an initial maximum matching found via Hopcroft-Karp.
  • Build a residual graph from the current matching: reverse the direction of matched edges, keep non-matched edges in their original direction. Alternating cycles in the original graph become directed cycles in this residual graph.
  • Find random directed cycles in the residual graph, swap the matched/unmatched edges in the original graph to create a new maximum matching.
  • Repeat this process, checking for duplicates, until you have hundreds of unique matchings.
Code Implementation (Adapted from Your Example)

Below is modified code that extends your existing setup to generate multiple distinct maximum matchings:

import igraph as ig
from scipy.sparse import random, find
from scipy import stats
from numpy.random import default_rng
import numpy as np
from igraph import Graph, plot
import random as rd

def generate_multiple_max_matches(g, num_matches=100):
    # Get initial maximum matching with Hopcroft-Karp
    initial_match = g.maximum_bipartite_matching()
    matches = [initial_match]
    
    # Split nodes into left (type 0) and right (type 1) partitions
    left_nodes = [v.index for v in g.vs if v['type'] == 0]
    right_nodes = [v.index for v in g.vs if v['type'] == 1]
    
    while len(matches) < num_matches:
        # Pick a random existing matching as the base for modification
        current_match = rd.choice(matches)
        # Create a dict for quick lookup of matched pairs (left -> right)
        match_map = {u: current_match.match_of(u) for u in left_nodes if current_match.match_of(u) is not None}
        
        # Build residual graph: reverse matched edges, keep non-matched edges as-is
        residual_edges = []
        for u in left_nodes:
            for v in g.neighbors(u):
                if match_map.get(u) == v:
                    # Matched edge: reverse direction (right -> left)
                    residual_edges.append((v, u))
                else:
                    # Non-matched edge: keep direction (left -> right)
                    residual_edges.append((u, v))
        
        residual_g = Graph(directed=True)
        residual_g.add_vertices(g.vcount())
        residual_g.add_edges(residual_edges)
        
        # Find up to 10 simple cycles in the residual graph (keeps things efficient)
        cycles = residual_g.simple_cycles(mode="all", limit=10)
        if not cycles:
            continue  # No cycles found, try another base matching
        
        # Pick a random cycle to modify
        cycle = rd.choice(cycles)
        # Bipartite graphs only have even-length cycles, skip any odd ones just in case
        if len(cycle) % 2 != 0:
            continue
        
        # Create new matching by swapping edges in the cycle
        new_match_map = match_map.copy()
        for i in range(0, len(cycle), 2):
            u, v = cycle[i], cycle[i+1]
            if u in left_nodes:
                # Assign left node u to right node v (overwriting existing match if needed)
                new_match_map[u] = v
            else:
                # Remove the match for the left node v (since u is a right node now matched elsewhere)
                if v in new_match_map:
                    del new_match_map[v]
        
        # Convert to igraph Matching object
        new_match_pairs = [(u, v) for u, v in new_match_map.items()]
        new_match = ig.Matching(g, new_match_pairs)
        
        # Check for duplicates before adding
        new_match_set = set(tuple(sorted(new_match.items())))
        duplicate = False
        for m in matches:
            if set(tuple(sorted(m.items()))) == new_match_set:
                duplicate = True
                break
        if not duplicate:
            matches.append(new_match)
    
    return matches

# Generate your sample bipartite graph
np.random.seed(7)
rng = default_rng()
rvs = stats.poisson(2).rvs
S = random(20, 20, density=0.35, random_state=rng, data_rvs=rvs)
triples = [*zip(*find(S))]
edges = [(triple[0], triple[1]+20) for triple in triples]
types = [0]*20 + [1]*20
g = Graph.Bipartite(types, edges)

# Generate 100 distinct maximum matchings
max_matches = generate_multiple_max_matches(g, num_matches=100)

# Visualize the 5th matching (adjust index to see others)
sample_match = max_matches[4]
visual_style = {
    "vertex_size": 20,
    "bbox": (600, 300),
    "vertex_label": range(g.vcount()),
    "vertex_color": "lightblue",
    "edge_width": [5 if e.target == sample_match.match_of(e.source) else 1.0 for e in g.es],
    "edge_color": ["red" if e.target == sample_match.match_of(e.source) else "black" for e in g.es]
}
plot(g, layout=g.layout_bipartite(), **visual_style)
Key Notes for Large Graphs
  • Duplicate Checking: Converting matchings to sorted tuples in a set is a simple way to avoid duplicates, which can happen if you cycle back to a previous matching.
  • Efficiency: Limiting the number of cycles found per iteration (limit=10) prevents the code from getting stuck searching for cycles in huge graphs. Randomly selecting base matchings and cycles also helps generate diverse matchings quickly.
  • Bipartite Cycle Guarantee: Bipartite graphs only contain even-length cycles, so we skip any odd-length ones to ensure we don't break the matching size.

内容的提问来源于stack exchange,提问作者byteful

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 18:17:32