本文目录导读:

我来详细讲解如何使用NetworkX实现稳定婚姻匹配算法(Gale-Shapley算法)。
什么是稳定婚姻问题
稳定婚姻问题是指:有n个男性和n个女性,每个人对异性都有偏好排序,需要找到一种配对方式,使得不存在"不稳定对"。
完整实现案例
基础实现
import networkx as nx
import matplotlib.pyplot as plt
def stable_marriage(men_preferences, women_preferences):
"""
实现Gale-Shapley稳定婚姻算法
参数:
men_preferences: dict, 每个男性的偏好列表
women_preferences: dict, 每个女性的偏好列表
返回:
matches: dict, 男性到女性的匹配
"""
# 初始化
free_men = list(men_preferences.keys()) # 未匹配的男性
matches = {} # 男性 -> 女性
woman_matches = {} # 女性 -> 男性
proposal_count = {men: 0 for men in men_preferences} # 每个男性已经求婚的次数
# 当还有未匹配的男性时继续
while free_men:
man = free_men[0] # 取第一个未匹配的男性
# 获取该男性的偏好列表
preferences = men_preferences[man]
# 如果还有未求婚的女性
if proposal_count[man] < len(preferences):
woman = preferences[proposal_count[man]]
proposal_count[man] += 1
# 如果该女性未匹配
if woman not in woman_matches:
# 匹配成功
matches[man] = woman
woman_matches[woman] = man
free_men.remove(man)
else:
# 该女性已匹配,比较当前匹配和新的求婚者
current_match = woman_matches[woman]
woman_pref = women_preferences[woman]
# 如果女性更喜欢新的追求者
if woman_pref.index(man) < woman_pref.index(current_match):
# 更换匹配
matches[man] = woman
woman_matches[woman] = man
free_men.remove(man)
free_men.append(current_match)
del matches[current_match]
else:
# 该男性已经向所有女性求婚过,但仍未匹配
free_men.remove(man)
return matches
def create_preference_graph(preferences, title="偏好关系图"):
"""创建偏好关系图"""
G = nx.DiGraph()
# 添加节点和边
for person, prefs in preferences.items():
for rank, pref in enumerate(prefs):
# 权重表示偏好程度,越小越偏好
G.add_edge(person, pref, weight=rank,
label=f"rank {rank+1}")
# 绘制图形
pos = nx.spring_layout(G, k=1, iterations=50)
plt.figure(figsize=(10, 8))
# 绘制节点
nx.draw_networkx_nodes(G, pos, node_color='lightblue',
node_size=500, alpha=0.8)
# 绘制边,根据权重调整颜色
edges = G.edges(data=True)
weights = [edge[2]['weight'] for edge in edges]
colors = plt.cm.RdYlGn([w/max(weights) for w in weights])
nx.draw_networkx_edges(G, pos, edge_color=colors,
arrowstyle='->', arrowsize=20, width=2)
# 添加标签
nx.draw_networkx_labels(G, pos, font_size=12, font_weight='bold')
plt.title(title)
plt.axis('off')
return G
def visualize_matching(matches, men_preferences, women_preferences):
"""可视化匹配结果"""
G = nx.Graph()
# 添加男性节点
for man in men_preferences.keys():
G.add_node(man, bipartite=0, color='lightblue', size=500)
# 添加女性节点
for woman in women_preferences.keys():
G.add_node(woman, bipartite=1, color='lightpink', size=500)
# 添加匹配边
for man, woman in matches.items():
G.add_edge(man, woman, color='red', width=3)
# 设置布局
pos = {}
men = list(men_preferences.keys())
women = list(women_preferences.keys())
# 男性在上方,女性在下方
for i, man in enumerate(men):
pos[man] = (i, 1)
for i, woman in enumerate(women):
pos[woman] = (i, 0)
plt.figure(figsize=(12, 6))
# 绘制节点
node_colors = ['lightblue' if node in men else 'lightpink'
for node in G.nodes()]
nx.draw_networkx_nodes(G, pos, node_color=node_colors,
node_size=500, alpha=0.8)
# 绘制边
edge_colors = ['red' if 'color' in G.edges[edge] and G.edges[edge]['color'] == 'red'
else 'gray' for edge in G.edges()]
edge_widths = [3 if 'width' in G.edges[edge] and G.edges[edge]['width'] == 3
else 1 for edge in G.edges()]
nx.draw_networkx_edges(G, pos, edge_color=edge_colors,
width=edge_widths, alpha=0.7)
# 添加标签
nx.draw_networkx_labels(G, pos, font_size=12, font_weight='bold')
plt.title("稳定婚姻匹配结果", fontsize=14)
plt.axis('off')
return G
# 示例数据
men_preferences = {
'张三': ['李婷', '王芳', '赵雪'],
'李四': ['王芳', '李婷', '赵雪'],
'王五': ['赵雪', '王芳', '李婷']
}
women_preferences = {
'李婷': ['张三', '李四', '王五'],
'王芳': ['李四', '张三', '王五'],
'赵雪': ['王五', '张三', '李四']
}
# 执行稳定婚姻匹配
matches = stable_marriage(men_preferences, women_preferences)
print("匹配结果:")
for man, woman in matches.items():
print(f"{man} <-> {woman}")
# 可视化偏好关系
print("\n男性偏好关系图:")
G_men = create_preference_graph(men_preferences, "男性偏好")
plt.show()
print("\n女性偏好关系图:")
G_women = create_preference_graph(women_preferences, "女性偏好")
plt.show()
# 可视化匹配结果
print("\n匹配结果可视化:")
G_matching = visualize_matching(matches, men_preferences, women_preferences)
plt.show()
高级功能实现
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
class StableMarriageSolver:
"""稳定婚姻问题求解器"""
def __init__(self, men_preferences, women_preferences):
self.men = list(men_preferences.keys())
self.women = list(women_preferences.keys())
self.men_preferences = men_preferences
self.women_preferences = women_preferences
self.matches = {}
def solve(self):
"""求解稳定婚姻问题"""
free_men = self.men.copy()
matches = {}
woman_matches = {}
proposal_count = {man: 0 for man in self.men}
while free_men:
man = free_men[0]
if proposal_count[man] < len(self.women):
woman = self.men_preferences[man][proposal_count[man]]
proposal_count[man] += 1
if woman not in woman_matches:
matches[man] = woman
woman_matches[woman] = man
free_men.remove(man)
else:
current = woman_matches[woman]
woman_pref = self.women_preferences[woman]
if woman_pref.index(man) < woman_pref.index(current):
matches[man] = woman
woman_matches[woman] = man
free_men.remove(man)
free_men.append(current)
del matches[current]
else:
free_men.remove(man)
self.matches = matches
return matches
def check_stability(self):
"""检查匹配是否稳定"""
if not self.matches:
print("请先运行solve()方法")
return False
for man, wife in self.matches.items():
man_pref = self.men_preferences[man]
woman_pref_index = man_pref.index(wife)
# 检查每个男性是否会与更偏好的女性形成不稳定对
for better_woman in man_pref[:woman_pref_index]:
better_woman_husband = {v: k for k, v in self.matches.items()}[better_woman]
better_woman_pref = self.women_preferences[better_woman]
# 检查这个女性是否也更喜欢当前男性
if better_woman_pref.index(man) < better_woman_pref.index(better_woman_husband):
print(f"发现不稳定对: ({man}, {better_woman})")
return False
print("匹配是稳定的!")
return True
def create_matching_graph(self):
"""创建匹配关系图"""
G = nx.Graph()
# 添加节点
for man in self.men:
G.add_node(man, type='man', color='blue')
for woman in self.women:
G.add_node(woman, type='woman', color='red')
# 添加匹配边
for man, woman in self.matches.items():
G.add_edge(man, woman, color='green', style='solid')
return G
def create_preference_matrix(self):
"""创建偏好矩阵"""
n = len(self.men)
matrix = np.zeros((n, n))
for i, man in enumerate(self.men):
for j, woman in enumerate(self.women):
if man in self.matches and self.matches[man] == woman:
matrix[i][j] = 1
return matrix
def visualize_all(self):
"""综合可视化"""
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
# 1. 匹配关系图
G = self.create_matching_graph()
pos = nx.spring_layout(G, k=2, iterations=50)
node_colors = ['lightblue' if G.nodes[node]['type'] == 'man'
else 'lightpink' for node in G.nodes()]
nx.draw(G, pos, ax=axes[0,0], with_labels=True,
node_color=node_colors, node_size=500,
edge_color='green', width=2, font_weight='bold')
axes[0,0].set_title('匹配关系图')
# 2. 偏好矩阵热图
matrix = self.create_preference_matrix()
im = axes[0,1].imshow(matrix, cmap='RdYlGn', aspect='auto')
axes[0,1].set_xticks(range(len(self.women)))
axes[0,1].set_yticks(range(len(self.men)))
axes[0,1].set_xticklabels(self.women)
axes[0,1].set_yticklabels(self.men)
axes[0,1].set_title('匹配矩阵')
plt.colorbar(im, ax=axes[0,1])
# 3. 男性偏好关系图
G_men = nx.DiGraph()
for man, prefs in self.men_preferences.items():
for rank, pref in enumerate(prefs):
G_men.add_edge(man, pref, weight=rank)
pos_men = nx.spring_layout(G_men, k=1)
edges = G_men.edges(data=True)
weights = [edge[2]['weight'] for edge in edges]
nx.draw(G_men, pos_men, ax=axes[1,0], with_labels=True,
node_color='lightblue', node_size=400,
edge_color=weights, edge_cmap=plt.cm.Blues,
width=2, font_weight='bold')
axes[1,0].set_title('男性偏好关系')
# 4. 女性偏好关系图
G_women = nx.DiGraph()
for woman, prefs in self.women_preferences.items():
for rank, pref in enumerate(prefs):
G_women.add_edge(woman, pref, weight=rank)
pos_women = nx.spring_layout(G_women, k=1)
edges_w = G_women.edges(data=True)
weights_w = [edge[2]['weight'] for edge in edges_w]
nx.draw(G_women, pos_women, ax=axes[1,1], with_labels=True,
node_color='lightpink', node_size=400,
edge_color=weights_w, edge_cmap=plt.cm.Reds,
width=2, font_weight='bold')
axes[1,1].set_title('女性偏好关系')
plt.tight_layout()
plt.show()
# 使用示例
if __name__ == "__main__":
# 定义偏好
men_prefs = {
'张三': ['李婷', '王芳', '赵雪'],
'李四': ['王芳', '李婷', '赵雪'],
'王五': ['赵雪', '王芳', '李婷']
}
women_prefs = {
'李婷': ['张三', '李四', '王五'],
'王芳': ['李四', '张三', '王五'],
'赵雪': ['王五', '张三', '李四']
}
# 创建求解器
solver = StableMarriageSolver(men_prefs, women_prefs)
# 解决问题
matches = solver.solve()
print("稳定婚姻匹配结果:")
for man, woman in matches.items():
print(f"{man} <-> {woman}")
# 检查稳定性
solver.check_stability()
# 可视化
solver.visualize_all()
随机生成测试数据
import random
def generate_random_preferences(n, seed=None):
"""生成随机偏好数据"""
if seed:
random.seed(seed)
men_names = [f'男{i+1}' for i in range(n)]
women_names = [f'女{i+1}' for i in range(n)]
men_preferences = {}
women_preferences = {}
for man in men_names:
prefs = women_names.copy()
random.shuffle(prefs)
men_preferences[man] = prefs
for woman in women_names:
prefs = men_names.copy()
random.shuffle(prefs)
women_preferences[woman] = prefs
return men_preferences, women_preferences
# 生成5个男性和5个女性的随机偏好
men_prefs, women_prefs = generate_random_preferences(5, seed=42)
print("随机生成的男性偏好:")
for man, prefs in men_prefs.items():
print(f"{man}: {prefs}")
print("\n随机生成的女性偏好:")
for woman, prefs in women_prefs.items():
print(f"{woman}: {prefs}")
# 求解
solver = StableMarriageSolver(men_prefs, women_prefs)
matches = solver.solve()
print("\n匹配结果:")
for man, woman in matches.items():
print(f"{man} <-> {woman}")
solver.check_stability()
关键点说明
- 算法核心:Gale-Shapley算法的实现
- NetworkX应用:使用图形表示偏好关系和匹配结果
- 稳定性检查:验证匹配结果是否稳定
- 可视化:多角度展示匹配结果
这个案例展示了如何使用NetworkX来建模和解决稳定婚姻问题,包括算法实现、图形可视化和结果验证。