用Python再现演化博弈经典:从方格网络到无标度网络的策略演化实验
1992年,Nowak和May在《自然》杂志发表的那篇开创性论文,彻底改变了人们对合作行为演化的理解。他们在一个简单的方格网络上运行囚徒困境博弈,发现空间结构能让合作者通过形成集群来抵御背叛者的入侵——这一发现直接挑战了传统博弈论中"均匀混合群体"的基本假设。今天,我们将用Python和NetworkX库,从零开始重现这个经典实验,并进一步扩展到更符合现实世界的Barabasi-Albert无标度网络。
1. 演化博弈与复杂网络的交叉简史
演化博弈论与复杂网络的结合,本质上是在回答一个根本问题:社会结构如何影响策略的传播?在传统博弈论模型中,所有参与者都被假定为可以随机相遇并互动(即"均匀混合"假设)。但现实中,我们的互动总是受限于社交圈、地理位置等各种网络结构。
Nowak-May实验的精妙之处在于,它用最简单的二维方格网络证明了:
- 合作者可以通过形成空间集群来保护自己免受背叛者侵害
- 局部互动模式能产生全局无法预测的涌现行为
- 网络拓扑结构本身可以成为合作演化的关键驱动力
随着复杂网络科学的发展,研究者们逐渐意识到,现实中的网络很少是规则的方格结构。Barabasi和Albert在1999年提出的无标度网络模型,因其节点度分布的幂律特性,更好地刻画了互联网、社交网络等真实系统的拓扑特征。这自然引出一个问题:网络结构的差异会如何影响合作行为的演化?
2. 环境配置与基础模型构建
2.1 工具链准备
我们需要以下Python库来实现这个实验:
import networkx as nx # 网络构建与分析 import numpy as np # 数值计算 import matplotlib.pyplot as plt # 可视化 from collections import defaultdict # 高效数据存储安装这些库的最简单方式是使用pip:
pip install networkx numpy matplotlib2.2 囚徒困境的收益矩阵
囚徒困境的核心在于收益矩阵的定义。我们采用Nowak-May论文中的经典参数化:
| 对手合作 | 对手背叛 | |
|---|---|---|
| 你合作 | 1 | 0 |
| 你背叛 | b | 0 |
其中b > 1是关键参数,表示背叛者剥削合作者获得的超额收益。在Nowak的原始实验中,b=1.8。
def calculate_payoff(strategy_self, strategy_opponent, b=1.8): if strategy_self == 1 and strategy_opponent == 1: # 双方合作 return 1 elif strategy_self == 1 and strategy_opponent == 0: # 自己合作,对方背叛 return 0 elif strategy_self == 0 and strategy_opponent == 1: # 自己背叛,对方合作 return b else: # 双方背叛 return 03. Nowak-May方格网络的Python实现
3.1 构建方格网络
Nowak-May实验使用的是最简单的二维周期性方格网络,每个节点有8个邻居(Moore邻域):
def create_lattice(size=50): G = nx.grid_2d_graph(size, size, periodic=True) return G3.2 策略更新规则
Nowak-May模型采用"模仿最优"策略更新规则:每个玩家比较自己与所有邻居的累计收益,然后在下轮采用收益最高者的策略。
def update_strategy_lattice(G, strategy, b): new_strategy = strategy.copy() for node in G.nodes(): neighbors = list(G.neighbors(node)) payoffs = defaultdict(int) # 计算所有邻居(包括自己)的累计收益 for n in [node] + neighbors: payoffs[n] = sum(calculate_payoff(strategy[n], strategy[o], b) for o in G.neighbors(n)) # 选择收益最高的策略 best_node = max(payoffs.keys(), key=lambda x: payoffs[x]) new_strategy[node] = strategy[best_node] return new_strategy3.3 可视化与实验结果
运行100代后的典型结果如下图所示:
def simulate_lattice(size=50, generations=100, b=1.8): G = create_lattice(size) strategy = {node: np.random.choice([0, 1]) for node in G.nodes()} for _ in range(generations): strategy = update_strategy_lattice(G, strategy, b) # 可视化 plt.figure(figsize=(10, 10)) nx.draw(G, pos={(x,y):(x,y) for x,y in G.nodes()}, node_color=[strategy[node] for node in G.nodes()], node_size=50, cmap=plt.cm.binary) plt.title(f"Nowak-May Model after {generations} generations (b={b})") plt.show()运行simulate_lattice(),你会看到合作者(白色)和背叛者(黑色)形成了复杂的空间模式,这正是Nowak和May观察到的"空间混沌"现象。
4. 扩展到Barabasi-Albert无标度网络
4.1 构建无标度网络
Barabasi-Albert模型通过优先连接机制生成具有幂律度分布的网络:
def create_ba_network(n=2500, m=5): G = nx.barabasi_albert_graph(n, m) return G4.2 适应无标度网络的策略更新
在异质性更强的无标度网络中,我们需要考虑节点度的影响。这里采用"概率模仿"规则:
def update_strategy_ba(G, strategy, b): new_strategy = strategy.copy() for node in G.nodes(): neighbors = list(G.neighbors(node)) if not neighbors: continue # 计算收益 payoffs = {} for n in [node] + neighbors: payoffs[n] = sum(calculate_payoff(strategy[n], strategy[o], b) for o in G.neighbors(n)) # 按收益差计算模仿概率 total_payoff = sum(payoffs.values()) probabilities = {n: max(0, payoffs[n] - payoffs[node]) for n in neighbors} sum_prob = sum(probabilities.values()) if sum_prob > 0: probabilities = {n: p/sum_prob for n,p in probabilities.items()} chosen = np.random.choice(list(probabilities.keys()), p=list(probabilities.values())) new_strategy[node] = strategy[chosen] return new_strategy4.3 对比实验与发现
运行两种网络上的模拟并比较合作频率:
def compare_networks(generations=100, b_values=np.linspace(1.0, 2.0, 11)): lattice_results = [] ba_results = [] for b in b_values: # 方格网络模拟 G_lattice = create_lattice() strategy_lattice = {node: np.random.choice([0, 1]) for node in G_lattice.nodes()} # 无标度网络模拟 G_ba = create_ba_network() strategy_ba = {node: np.random.choice([0, 1]) for node in G_ba.nodes()} for _ in range(generations): strategy_lattice = update_strategy_lattice(G_lattice, strategy_lattice, b) strategy_ba = update_strategy_ba(G_ba, strategy_ba, b) lattice_results.append(sum(strategy_lattice.values())/len(strategy_lattice)) ba_results.append(sum(strategy_ba.values())/len(strategy_ba)) # 绘制结果 plt.plot(b_values, lattice_results, 'o-', label='Lattice Network') plt.plot(b_values, ba_results, 's-', label='Scale-free Network') plt.xlabel('b (temptation to defect)') plt.ylabel('Frequency of Cooperation') plt.legend() plt.grid(True) plt.show()这个对比揭示了几个关键发现:
- 在两种网络中,合作频率都随背叛诱惑b的增加而下降
- 无标度网络在较高b值时能维持更高的合作水平
- 网络异质性(少数高度节点)为合作提供了额外的保护机制
5. 进阶探索与参数调优
5.1 关键参数的影响
- 背叛诱惑b:控制博弈的紧张程度
- 网络平均度:影响局部互动的范围
- 初始合作频率:检验结果的鲁棒性
5.2 其他策略更新规则
除了模仿最优,还可以实现以下规则:
# 模仿概率与收益差成正比 def fermi_update(G, strategy, node, b, K=0.1): neighbors = list(G.neighbors(node)) if not neighbors: return strategy[node] opponent = np.random.choice(neighbors) payoff_diff = (sum(calculate_payoff(strategy[opponent], strategy[o], b) for o in G.neighbors(opponent)) - sum(calculate_payoff(strategy[node], strategy[o], b) for o in G.neighbors(node))) probability = 1 / (1 + np.exp(-payoff_diff / K)) return strategy[opponent] if np.random.random() < probability else strategy[node]5.3 性能优化技巧
对于大规模网络模拟,可以采用以下优化:
# 使用稀疏矩阵存储大型网络 from scipy import sparse def sparse_adjacency_matrix(G): return nx.adjacency_matrix(G)6. 应用案例:社交网络中的信息传播
将模型稍作修改,可以研究社交网络中的谣言传播:
def rumor_spreading_simulation(G, initial_infected=0.1, generations=50): # 状态:0=易感,1=传播,2=免疫 status = {node: 0 for node in G.nodes()} infected = np.random.choice(G.nodes(), int(initial_infected*len(G)), replace=False) for node in infected: status[node] = 1 for _ in range(generations): new_status = status.copy() for node in G.nodes(): if status[node] == 1: # 传播者 for neighbor in G.neighbors(node): if status[neighbor] == 0 and np.random.random() < 0.5: new_status[neighbor] = 1 new_status[node] = 2 # 变为免疫 status = new_status return sum(1 for s in status.values() if s == 2) / len(status)