游戏简介与算法核心
囚徒困境是博弈论中最经典的模型之一。两名嫌疑人被分别审讯,各自面临”合作”(保持沉默)或”背叛”(揭发对方)的选择。单次博弈中,背叛是纳什均衡;但在重复博弈中,合作可能通过策略互动而涌现。本文用Java实现重复囚徒困境的演化模拟,讲解收益矩阵计算、策略对抗评估与复制动态过程。
收益矩阵与博弈规则
经典囚徒困境的收益结构(T > R > P > S):
| 玩家B合作 | 玩家B背叛 | |
|---|---|---|
| 玩家A合作 | R=3, R=3 | S=0, T=5 |
| 玩家A背叛 | T=5, S=0 | P=1, P=1 |
其中:T(诱惑)= 5,R(奖励)= 3,P(惩罚)= 1,S( sucker)= 0。满足 T > R > P > S 且 2R > T + S。
import java.util.*;
/**
* 行动枚举
*/
enum Action {
COOPERATE('C'), DEFECT('D');
private final char symbol;
Action(char symbol) { this.symbol = symbol; }
public char getSymbol() { return symbol; }
}
/**
* 回合结果记录
*/
class RoundResult {
final Action actionA;
final Action actionB;
final int payoffA;
final int payoffB;
RoundResult(Action actionA, Action actionB, int payoffA, int payoffB) {
this.actionA = actionA;
this.actionB = actionB;
this.payoffA = payoffA;
this.payoffB = payoffB;
}
@Override
public String toString() {
return String.format("A:%s(%d) vs B:%s(%d)",
actionA.getSymbol(), payoffA, actionB.getSymbol(), payoffB);
}
}
/**
* 收益矩阵
*/
class PayoffMatrix {
private static final int T = 5; // Temptation
private static final int R = 3; // Reward
private static final int P = 1; // Punishment
private static final int S = 0; // Sucker
/**
* 获取收益 (player1, player2)
*/
public static int[] getPayoffs(Action a1, Action a2) {
if (a1 == Action.COOPERATE && a2 == Action.COOPERATE) return new int[]{R, R};
if (a1 == Action.COOPERATE && a2 == Action.DEFECT) return new int[]{S, T};
if (a1 == Action.DEFECT && a2 == Action.COOPERATE) return new int[]{T, S};
return new int[]{P, P}; // both defect
}
public static int getT() { return T; }
public static int getR() { return R; }
public static int getP() { return P; }
public static int getS() { return S; }
}
策略接口与经典策略实现
/**
* 策略接口:每轮根据历史记录决定行动
*/
interface Strategy {
String getName();
Action chooseAction(List<RoundResult> history, boolean isPlayerA);
Strategy cloneStrategy();
}
/**
* 始终合作策略
*/
class AlwaysCooperate implements Strategy {
public String getName() { return "AlwaysCooperate"; }
public Action chooseAction(List<RoundResult> history, boolean isPlayerA) {
return Action.COOPERATE;
}
public Strategy cloneStrategy() { return new AlwaysCooperate(); }
}
/**
* 始终背叛策略
*/
class AlwaysDefect implements Strategy {
public String getName() { return "AlwaysDefect"; }
public Action chooseAction(List<RoundResult> history, boolean isPlayerA) {
return Action.DEFECT;
}
public Strategy cloneStrategy() { return new AlwaysDefect(); }
}
/**
* 随机策略
*/
class RandomStrategy implements Strategy {
private final Random random = new Random();
private final double cooperateProb;
public RandomStrategy(double cooperateProb) {
this.cooperateProb = cooperateProb;
}
public String getName() {
return "Random(" + cooperateProb + ")";
}
public Action chooseAction(List<RoundResult> history, boolean isPlayerA) {
return random.nextDouble() < cooperateProb ? Action.COOPERATE : Action.DEFECT;
}
public Strategy cloneStrategy() {
return new RandomStrategy(cooperateProb);
}
}
/**
* Tit-for-Tat策略:第一轮合作,之后模仿对方上一步行动
* 这是Robert Axelrod tournament的冠军策略
*/
class TitForTat implements Strategy {
public String getName() { return "TitForTat"; }
public Action chooseAction(List<RoundResult> history, boolean isPlayerA) {
if (history.isEmpty()) return Action.COOPERATE;
// 模仿对方上一步的行动
RoundResult last = history.get(history.size() - 1);
Action opponentLastAction = isPlayerA ? last.actionB : last.actionA;
return opponentLastAction;
}
public Strategy cloneStrategy() { return new TitForTat(); }
}
/**
* Tit-for-Two-Tats:更宽容的版本,只有对方连续背叛两次才报复
*/
class TitForTwoTats implements Strategy {
public String getName() { return "TitForTwoTats"; }
public Action chooseAction(List<RoundResult> history, boolean isPlayerA) {
if (history.size() < 2) return Action.COOPERATE;
RoundResult last1 = history.get(history.size() - 1);
RoundResult last2 = history.get(history.size() - 2);
Action opp1 = isPlayerA ? last1.actionB : last1.actionA;
Action opp2 = isPlayerA ? last2.actionB : last2.actionA;
if (opp1 == Action.DEFECT && opp2 == Action.DEFECT) {
return Action.DEFECT;
}
return Action.COOPERATE;
}
public Strategy cloneStrategy() { return new TitForTwoTats(); }
}
/**
* Grudger(记仇者):一直合作直到对方背叛,之后永远背叛
*/
class Grudger implements Strategy {
private boolean grudge = false;
public String getName() { return "Grudger"; }
public Action chooseAction(List<RoundResult> history, boolean isPlayerA) {
if (!history.isEmpty()) {
RoundResult last = history.get(history.size() - 1);
Action opponentLast = isPlayerA ? last.actionB : last.actionA;
if (opponentLast == Action.DEFECT) grudge = true;
}
return grudge ? Action.DEFECT : Action.COOPERATE;
}
public Strategy cloneStrategy() { return new Grudger(); }
}
/**
* Pavlov(胜留败走):如果上一轮收益高则重复行动,否则改变行动
*/
class Pavlov implements Strategy {
public String getName() { return "Pavlov"; }
public Action chooseAction(List<RoundResult> history, boolean isPlayerA) {
if (history.isEmpty()) return Action.COOPERATE;
RoundResult last = history.get(history.size() - 1);
int myPayoff = isPlayerA ? last.payoffA : last.payoffB;
Action myLastAction = isPlayerA ? last.actionA : last.actionB;
// R=3, T=5 是高收益;P=1, S=0 是低收益
boolean highPayoff = (myPayoff == PayoffMatrix.getR() ||
myPayoff == PayoffMatrix.getT());
return highPayoff ? myLastAction :
(myLastAction == Action.COOPERATE ? Action.DEFECT : Action.COOPERATE);
}
public Strategy cloneStrategy() { return new Pavlov(); }
}
重复博弈对战引擎
/**
* 重复博弈引擎
*/
class RepeatedGame {
/**
* 执行一轮单次囚徒困境
*/
public RoundResult playRound(Strategy s1, Strategy s2,
List<RoundResult> history1,
List<RoundResult> history2) {
Action a1 = s1.chooseAction(history1, true);
Action a2 = s2.chooseAction(history2, false);
int[] payoffs = PayoffMatrix.getPayoffs(a1, a2);
return new RoundResult(a1, a2, payoffs[0], payoffs[1]);
}
/**
* 执行多轮重复博弈,返回双方总收益
*/
public int[] playRepeatedGame(Strategy s1, Strategy s2, int rounds) {
List<RoundResult> history1 = new ArrayList<>();
List<RoundResult> history2 = new ArrayList<>();
int total1 = 0, total2 = 0;
for (int i = 0; i < rounds; i++) {
RoundResult result = playRound(s1, s2, history1, history2);
history1.add(result);
history2.add(result);
total1 += result.payoffA;
total2 += result.payoffB;
}
return new int[]{total1, total2};
}
/**
* 执行带有终止概率的重复博弈(模拟无限重复)
*/
public int[] playIteratedGame(Strategy s1, Strategy s2, double continuationProb) {
List<RoundResult> history1 = new ArrayList<>();
List<RoundResult> history2 = new ArrayList<>();
int total1 = 0, total2 = 0;
Random random = new Random();
while (random.nextDouble() < continuationProb) {
RoundResult result = playRound(s1, s2, history1, history2);
history1.add(result);
history2.add(result);
total1 += result.payoffA;
total2 += result.payoffB;
}
return new int[]{total1, total2};
}
}
演化博弈与复制动态
/**
* 策略种群中的个体
*/
class Agent {
Strategy strategy;
int score;
int population;
Agent(Strategy strategy, int population) {
this.strategy = strategy;
this.population = population;
this.score = 0;
}
}
/**
* 演化模拟器:模拟策略种群的竞争与演化
*/
class EvolutionSimulator {
private final List<Agent> population = new ArrayList<>();
private final RepeatedGame game = new RepeatedGame();
private final int roundsPerEncounter;
public EvolutionSimulator(int roundsPerEncounter) {
this.roundsPerEncounter = roundsPerEncounter;
}
public void addStrategy(Strategy strategy, int initialPop) {
population.add(new Agent(strategy, initialPop));
}
/**
* 执行一代演化:所有个体两两对战,计算适应度,然后按适应度比例复制
*/
public void evolveGeneration() {
int totalPop = population.stream().mapToInt(a -> a.population).sum();
// 计算每个策略的平均得分
for (Agent agent : population) {
agent.score = 0;
}
for (int i = 0; i < population.size(); i++) {
for (int j = 0; j < population.size(); j++) {
Agent a1 = population.get(i);
Agent a2 = population.get(j);
// 克隆策略以保持状态独立
Strategy s1 = a1.strategy.cloneStrategy();
Strategy s2 = a2.strategy.cloneStrategy();
int[] result = game.playRepeatedGame(s1, s2, roundsPerEncounter);
// 加权平均(考虑种群比例)
a1.score += result[0] * a2.population;
}
// 归一化
if (totalPop > 0) {
agent.score /= totalPop;
}
}
// 复制动态:按相对适应度更新种群比例
int avgScore = population.stream().mapToInt(a -> a.score).sum() / population.size();
for (Agent agent : population) {
if (avgScore > 0) {
double fitness = (double) agent.score / avgScore;
agent.population = (int) (agent.population * fitness);
agent.population = Math.max(1, agent.population); // 保证不灭绝
}
}
}
/**
* 运行多代演化
*/
public void runSimulation(int generations) {
System.out.println("=== 囚徒困境演化模拟 ===");
System.out.println("每代对战轮数: " + roundsPerEncounter);
System.out.println();
// 打印初始状态
printPopulation(0);
for (int gen = 1; gen <= generations; gen++) {
evolveGeneration();
if (gen % 10 == 0 || gen == 1) {
printPopulation(gen);
}
}
}
private void printPopulation(int generation) {
System.out.println("第 " + generation + " 代:");
int total = population.stream().mapToInt(a -> a.population).sum();
for (Agent agent : population) {
double pct = total > 0 ? (double) agent.population / total * 100 : 0;
System.out.printf(" %-20s: %4d (%.1f%%) 平均得分: %d%n",
agent.strategy.getName(), agent.population, pct, agent.score);
}
System.out.println();
}
public List<Agent> getPopulation() { return population; }
}
策略锦标赛与评估
/**
* 策略锦标赛:Round-Robin循环赛
*/
class Tournament {
private final RepeatedGame game = new RepeatedGame();
private final int rounds;
public Tournament(int rounds) {
this.rounds = rounds;
}
/**
* 运行循环赛并打印结果矩阵
*/
public void runRoundRobin(List<Strategy> strategies) {
int n = strategies.size();
int[][] scores = new int[n][n];
System.out.println("=== 策略循环赛结果 ===");
System.out.println("每轮对战 " + rounds + " 回合\n");
// 表头
System.out.printf("%-20s", "策略");
for (int i = 0; i < n; i++) {
System.out.printf("%12s", strategies.get(i).getName().substring(0,
Math.min(12, strategies.get(i).getName().length())));
}
System.out.printf("%12s%n", "总分");
for (int i = 0; i < n; i++) {
System.out.printf("%-20s", strategies.get(i).getName());
int total = 0;
for (int j = 0; j < n; j++) {
Strategy s1 = strategies.get(i).cloneStrategy();
Strategy s2 = strategies.get(j).cloneStrategy();
int[] result = game.playRepeatedGame(s1, s2, rounds);
scores[i][j] = result[0];
total += result[0];
System.out.printf("%12d", result[0]);
}
System.out.printf("%12d%n", total);
}
// 打印排名
System.out.println("\n=== 排名 ===");
List<int[]> rankings = new ArrayList<>();
for (int i = 0; i < n; i++) {
int total = 0;
for (int j = 0; j < n; j++) total += scores[i][j];
rankings.add(new int[]{i, total});
}
rankings.sort((a, b) -> b[1] - a[1]);
for (int rank = 0; rank < rankings.size(); rank++) {
int idx = rankings.get(rank)[0];
int score = rankings.get(rank)[1];
System.out.printf("%d. %-20s 总分: %d%n",
rank + 1, strategies.get(idx).getName(), score);
}
}
}
主程序与演示
public class PrisonersDilemmaMain {
public static void main(String[] args) {
// 1. 演示单次博弈
System.out.println("=== 单次囚徒困境 ===");
RepeatedGame game = new RepeatedGame();
Strategy cooperate = new AlwaysCooperate();
Strategy defect = new AlwaysDefect();
int[] singleResult = game.playRepeatedGame(cooperate, defect, 1);
System.out.println("AlwaysCooperate vs AlwaysDefect: " +
singleResult[0] + " vs " + singleResult[1]);
System.out.println("纳什均衡: 双方都选择背叛,各得 " + PayoffMatrix.getP() + "\n");
// 2. 演示重复博弈中Tit-for-Tat的优势
System.out.println("=== 重复博弈 (100轮) ===");
Strategy tft = new TitForTat();
int[] repeatResult = game.playRepeatedGame(tft, cooperate, 100);
System.out.println("TitForTat vs AlwaysCooperate: " +
repeatResult[0] + " vs " + repeatResult[1]);
int[] repeatResult2 = game.playRepeatedGame(tft, defect, 100);
System.out.println("TitForTat vs AlwaysDefect: " +
repeatResult2[0] + " vs " + repeatResult2[1]);
int[] repeatResult3 = game.playRepeatedGame(tft, tft, 100);
System.out.println("TitForTat vs TitForTat: " +
repeatResult3[0] + " vs " + repeatResult3[1] + "\n");
// 3. 策略锦标赛
System.out.println("=== 策略锦标赛 ===");
List<Strategy> strategies = Arrays.asList(
new AlwaysCooperate(),
new AlwaysDefect(),
new TitForTat(),
new TitForTwoTats(),
new Grudger(),
new Pavlov(),
new RandomStrategy(0.5)
);
Tournament tournament = new Tournament(200);
tournament.runRoundRobin(strategies);
// 4. 演化模拟
System.out.println("\n=== 演化博弈模拟 ===");
EvolutionSimulator simulator = new EvolutionSimulator(100);
simulator.addStrategy(new AlwaysCooperate(), 20);
simulator.addStrategy(new AlwaysDefect(), 20);
simulator.addStrategy(new TitForTat(), 20);
simulator.addStrategy(new Grudger(), 20);
simulator.addStrategy(new Pavlov(), 20);
simulator.runSimulation(50);
}
}
算法复杂度分析
| 操作 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 单轮博弈 | O(1) | O(1) |
| N轮重复博弈 | O(N) | O(N) |
| 循环赛(K个策略) | O(K² × N) | O(K²) |
| 单代演化 | O(K² × N × P²) | O(K × P) |
| G代演化 | O(G × K² × N × P²) | O(K × P) |
其中K=策略种类数,N=每轮对战的回合数,P=种群规模。
总结
本文实现了重复囚徒困境的完整模拟框架,核心算法包括:
- 收益矩阵:形式化囚徒困境的收益结构,R > P > S 且 T > R 的条件确保单次博弈中背叛是均衡
- 策略设计:实现了AlwaysCooperate、AlwaysDefect、Tit-for-Tat、Grudger、Pavlov等经典策略
- 重复博弈:通过多轮交互历史让策略可以”学习”和”报复”,合作在重复博弈中成为可能
- 复制动态:模拟自然选择过程,适应度高的策略种群比例增加,观察合作策略是否能演化稳定
Tit-for-Tat策略的成功揭示了合作的演化条件:善良(不首先背叛)、报复(对背叛予以回击)、宽容(背叛后愿意恢复合作)、清晰(策略简单易懂)。这四个特性使其在Axelrod的计算机锦标赛中两次夺冠。