提交 9686e7e4 编写于 作者: W wizardforcel

12.

上级 5eb05d07
......@@ -55,7 +55,7 @@
事实证明,引发利他主义问题的囚徒困境,也可能有助于解决问题。
## 12.3 囚徒困境锦标
## 12.3 囚徒困境
在 20 世纪 70 年代后期,密歇根大学的政治学家罗伯特阿克塞尔罗德(Robert Axelrod)组织了一场比赛来比较囚徒困境(PD)的策略。
......@@ -63,9 +63,9 @@
在 Axelrod 的比赛中,一个简单的策略出人意料地好,称为“针锋相对”,即 TFT,TFT 在第一轮迭代比赛中总是合作;之后,它会复制上一轮对手所做的任何事情。对手继续合作,TFT 保持合作,如果对手任何时候都背叛,下一轮 TFT 背叛,但如果对手变回合作,TFT 也会合作。
这些锦标赛的更多信息,以及 TFT 为何如此出色的解释,请参阅以下视频:<https://www.youtube.com/watch?v=BOvAbjfJ0x0>
这些赛的更多信息,以及 TFT 为何如此出色的解释,请参阅以下视频:<https://www.youtube.com/watch?v=BOvAbjfJ0x0>
看看这些锦标赛中表现出色的策略,Alexrod 发现了他们倾向于分享的特点:
看看这些赛中表现出色的策略,Alexrod 发现了他们倾向于分享的特点:
+ 善良:表现好的策略在第一轮比赛中合作,并且通常会在随后的几轮中合作。
+ 报复:始终合作的策略,并不如如果对手背叛就报复的策略好。
......@@ -134,4 +134,125 @@ def mutate(self):
return values
```
既然我们有了智能体,我们还需要锦标赛。
既然我们有了智能体,我们还需要比赛。
## 12.5 `Tournament`
`Tournament`类封装了 PD 比赛的细节:
```py
payoffs = {('C', 'C'): (3, 3),
('C', 'D'): (0, 5),
('D', 'C'): (5, 0),
('D', 'D'): (1, 1)}
num_rounds = 6
def play(self, agent1, agent2):
agent1.reset()
agent2.reset()
for i in range(self.num_rounds):
resp1 = agent1.respond(agent2)
resp2 = agent2.respond(agent1)
pay1, pay2 = self.payoffs[resp1, resp2]
agent1.append(resp1, pay1)
agent2.append(resp2, pay2)
return agent1.score, agent2.score
```
`payoffs`是一个字典,将从智能体的选择映射为奖励。例如,如果两个智能体合作,他们每个得到 3 分。如果一个背叛而另一个合作,背叛者得到 5 分,而合作者得到 0 分。如果他们都背叛,每个都会得到 1 分。这些是 Axelrod 在他的比赛中使用的收益。
`play `运行几轮 PD 游戏。它使用`Agent`类中的以下方法:
+ `reset`:在第一轮之前初始化智能体,重置他们的分数和他们的回应的历史记录。
+ `respond`:考虑到对手之前的回应,向每个智能体询问回应。
+ `append`:通过存储选项,并将连续轮次的分数相加,来更新每个智能体。
在给定的回合数之后,`play`将返回每个智能体的总分数。我选择了`num_rounds = 6`,以便每个基因型的元素都以大致相同的频率访问。第一个元素仅在第一轮访问,或在六分之一的时间内访问。接下来的两个元素只能在第二轮中访问,或者每个十二分之一。最后四个元素在六分之一时间内访问,平均每次访问六次,或者平均每个六分之一。
`Tournament`提供了第二种方法,即`melee`,确定哪些智能体互相竞争:
```py
def melee(self, agents, randomize=True):
if randomize:
agents = np.random.permutation(agents)
n = len(agents)
i_row = np.arange(n)
j_row = (i_row + 1) % n
totals = np.zeros(n)
for i, j in zip(i_row, j_row):
agent1, agent2 = agents[i], agents[j]
score1, score2 = self.play(agent1, agent2)
totals[i] += score1
totals[j] += score2
for i in i_row:
agents[i].fitness = totals[i] / self.num_rounds / 2
```
`melee`接受一个智能体列表和一个布尔值`randomize`,它决定了每个智能体每次是否与同一邻居竞争,或者匹配是否随机化。
`i_row``j_row`包含匹配的索引。 `totals`包含每个智能体的总分数。
在循环内部,我们选择两个智能体,调用`play`和更新`totals`。 最后,我们计算每个智能体获得的,每轮和每个对手的平均点数,并将结果存储在每个智能体的`fitness `属性中。
## 12.6 `Simulation`
本章的`Simulation`类基于第?章的中的那个;唯一的区别是`__init__``step`
这是`__init__`方法:
```py
class PDSimulation(Simulation):
def __init__(self, tournament, agents):
self.tournament = tournament
self.agents = np.asarray(agents)
self.instruments = []
```
`Simulation`对象包含一个`Tournament`对象,一系列的智能体和一系列的`Instrument`对象(就像第?章中一样)。
以下是`step`方法:
```py
def step(self):
self.tournament.melee(self.agents)
Simulation.step(self)
```
此版本的`step`使用`Tournament.melee`,它为每个智能体设置`fitness`属性;然后它调用父类的`step`方法,父类来自第?章:
```py
# class Simulation
def step(self):
n = len(self.agents)
fits = self.get_fitnesses()
# see who dies
index_dead = self.choose_dead(fits)
num_dead = len(index_dead)
# replace the dead with copies of the living
replacements = self.choose_replacements(num_dead, fits)
self.agents[index_dead] = replacements
# update any instruments
self.update_instruments()
```
`Simulation.step`将智能体的适应性收集到一个数组中; 然后它会调用`choose_dead`来决定哪些智能体死掉,并用`choose_replacements`来决定哪些智能体繁殖。
我的模拟包含生存差异,就像第?章那样,但不包括繁殖差异。 你可以在本章的笔记本上看到细节。 作为练习之一,您将有机会探索繁殖差异的效果。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册