<< 返回本书主页<< Back to the afterword page
Jan. 2022
4 博弈论基础概念4.1 背景问题博弈问题作为理论抽象博弈问题的求解4.2 计算实践:多人重复囚徒困境(IPD)博弈循环赛4.2.1 作业描述与算法思路4.2.2 编程实现与要点说明策略函数博弈循环赛4.2.3 结果与分析
博弈论是社会科学经典理论,同图论一样,博弈论可以用于对现实世界的一类现象进行抽象。
李老师先从最经典的“囚徒困境”讲起。两疑犯被警察抓住,分开关押。警察怀疑他们和一抢劫案有关,但没充足的证据。然而,他们都拒捕的事实也是可判刑的。开始审问,两疑犯被分别告知以下后果:
同时也告知:另一方也正在被告知这样的后果及你也知道这些。
两个囚徒之间的博弈可以由以下的收益矩阵表示:
囚徒1 | |||
---|---|---|---|
抵赖 | 坦白 | ||
囚徒2 | 抵赖 | -1, -1 | -10, 0 |
坦白 | 0, -10 | -4, -4 |
这里的要点是,每个人的收益不仅受自己的选择影响,而且受对方的选择影响。收益矩阵的不同赋值(关键是不同选择的收益的序数),决定了博弈的结果。
除了囚徒困境以外,博弈有多种类型。按照收益矩阵的不同赋值,博弈还有协调博弈和零和博弈(定义在此处略过),等等。除了两个参与者之间的博弈,博弈还可能在多个人之间发生。例如,在拍卖活动中,我的收益也取决于别人的出价。
总而言之,博弈有以下三个要素:
对于任意一个博弈,我们关心:
求解博弈问题有多种方法。一种普遍的关注是找出博弈的均衡:对每个参与人而言,单方面更换策略已不能更好。根据纳什的研究,任何有穷博弈都至少有一个均衡,它有可能是纯策略均衡,也有可能是混合策略均衡(以给定的概率分别采取多种策略)。
要进行简单博弈推理,我们首先有以下几个理论假设:
在前面囚徒困境的例子中,
囚徒1 | |||
---|---|---|---|
抵赖 | 坦白 | ||
囚徒2 | 抵赖 | -1, -1 | -10, 0 |
坦白 | 0, -10 | -4, -4 |
尽管两者都抵赖总福利最大(
除了双方都有严格占优策略的情况之外,达成均衡有多种方式。李老师在课上给我们举了几个例子,我们还做了几个求解练习。由于这些内容与本节课的计算作业关系不大,故仅列举在此。
为了研究重复囚徒困境问题,假设招募了全班共
我们被告知博弈矩阵(如下图)以及参加的总人数,还被告知,比赛采用串行方式,即两个人把
玩家k | |||
---|---|---|---|
合作C | 背叛D | ||
玩家j | 合作C | 3, 3 | 0, 5 |
背叛D | 5, 0 | 1, 1 |
我们要以一个函数的方式提供自己的策略。该函数的输入是自己和对手到当前为止已给出的行为(策略的一部分),返回自己下一次的行为。总得分是我参与的所有博弈(
每个人给出自己的策略函数后,我们还需要写程序模拟一个循环赛程序(调用那些函数),试用不同的
为了设计出得分高的策略,我的函数要分析自己和对手到当前为止已给出的行为,预测对方的下一次行为。给出自己的反应时,我们还要考虑到,自己如此行动会成为此后博弈的已知信息,这会不会影响未来的合作?总之,我们需要预测对方的行动,做出最有利于多次博弈总收益的回应。最好是能猜出对方的策略函数。比如说,“一报还一报”策略可能被很多人采纳,那么我可以在我的策略函数中设置一些判断条件。如果对方真的采用“一报还一报”策略,最有利于我的策略就是永远选择合作。模拟循环赛的程序并不难,只有判断最佳应答策略时程序稍微有些复杂,具体的算法见后。
每个人的策略函数,输入自己到当前为止已给出的行为me
和对手到当前为止已给出的行为him
,返回下一次的行为合作C
或背叛D
。
一个最典型的策略函数是“一报还一报”。如果对方上一次选择合作,亦即if him[i-1]=='C'
,我这一次就选择合作'C'
;否则就选择背叛。
xxxxxxxxxx
81def p7(i,me,him):
2 if i == 0:
3 return 'C'
4 else:
5 if him[i-1]=='C':
6 return 'C'
7 else:
8 return 'D'
老师提供了一个devil
策略。如果对方上一次选择合作,devil就选择背叛;对方上一次背叛,devil就合作。
xxxxxxxxxx
81def devil(i,me,him):
2 if i==0:
3 return 'D'
4 else:
5 if him[i-1]=='C':
6 return 'D'
7 else:
8 return 'C'
我的策略函数则稍微复杂一些。我在函数里加入了一些判断条件,试图预测对方的策略,从而给出自己的反应。
xxxxxxxxxx
11def p10(i, me, him):
前四次我都选择合作,从而获取对方的四次反应数据。
xxxxxxxxxx
21 if i in [0,1,2,3]:
2 return 'C'
接着,我开始预测对方的策略函数。
如果对方第三次以后总是选择合作,我就认为ta采取的是“天真策略”('innocent'
)。当然,我前四次都选择合作,对方也选择合作很正常,这个判断条件会让我第五次选择背叛。但这只会损害我在一轮里的收益,这是获取对方反应数据的必要成本。如果我第五次选择了背叛,对方仍然不选择背叛,认为对方采取的是“天真策略”('innocent'
)就比较合理了。
xxxxxxxxxx
51 else:
2 if 'D' not in him[2:]:
3 flag = 'innocent'
4 if flag == 'innocent':
5 return 'D'
接着,我开始验证对方是否采取了“一报还一报”策略。关键在于分析对方采取合作时的情景:对方为什么愿意信任我,选择合作?
cnt1_1
,当我上一轮选择合作时对方下一轮选择合作的次数存储在cnt1_2
,当cnt1_2 / (cnt1_1 - 1) > 0.9
时,我认为我上一轮选择合作能够触发对方下一轮选择合作。这个策略被我称为's-retri'
(短时报复策略)。cnt2_1
,ta选择合作时,有cnt2_2
次,我过去行为的众数也是合作。当cnt2_2 / cnt2_1 > 0.9
时,我认为,我过去经常选择合作能够触发对方下一轮选择合作。这个策略被我称为'l-retri'
(长时报复策略)。xxxxxxxxxx
201 flag = None
2 cnt1_1 = 0
3 cnt1_2 = 0
4 cnt2_1 = 0
5 cnt2_2 = 0
6 for j in range(i-1):
7 if me[j-1] == 'C':
8 cnt1_1 += 1
9 if me[j-1] == him[j]:
10 cnt1_2 += 1
11 elif him[j] == 'C':
12 cnt2_1 += 1
13 if j > 2 and max(set(me), key = me.count) == him[j]:
14 cnt2_2 += 1
15
16 elif cnt1_2 / (cnt1_1 - 1) > 0.9 and cnt1_2 >= 2:
17 flag = 's-retri'
18 elif cnt2_1 > 0:
19 if cnt2_2 / cnt2_1 > 0.9:
20 flag = 'l-retri'
如果我无法判断对方的策略,保险起见,我选择背叛。
xxxxxxxxxx
21 if flag == None:
2 return 'D'
如果我判断对方的策略是报复策略(不论是长时还是短时),我都选择合作,以期ta下回也会和我合作。
xxxxxxxxxx
21 elif flag in ['s-retri','l-retri']:
2 return 'C'
11位同学和devil的策略函数集合在strategy.py
里,先跨文件调用:
xxxxxxxxxx
21import strategy
2strategies = [strategy.p1, strategy.p2, strategy.p3, strategy.p4, strategy.p5, strategy.p6, strategy.p7, strategy.p8, strategy.p9, strategy.p10, strategy.p11, strategy.devil]
首先定义一个函数game_dual
。给定两个玩家,模拟二者之间的多轮重复博弈,按照收益矩阵计算两个人各自的分数。
xxxxxxxxxx
271def game_dual(n,i,j):
2 me = []
3 me_score = 0
4 me_str = strategies[i-1]
5 him = []
6 him_score = 0
7 him_str = strategies[j-1]
8 for k in range(n):
9 # actions
10 me_react = me_str(k,me,him)
11 him_react = him_str(k,him,me)
12 me.append(me_react)
13 him.append(him_react)
14 # scores
15 if me_react == 'D' and him_react == 'D':
16 me_score += 1
17 him_score += 1
18 elif me_react == 'C' and him_react == 'D':
19 me_score += 0
20 him_score += 5
21 elif him_react == 'C' and me_react == 'D':
22 him_score += 0
23 me_score += 5
24 else:
25 him_score += 3
26 me_score += 3
27 return me_score, him_score
定义函数game
,以便改变循环赛设定(轮数score_dict
里。例如,i
和j
博弈的结果就是score_dict[i][j]
。
首先,统一计算所有玩家相互博弈后的总分数(存储在scores
里),输出结果。
xxxxxxxxxx
241def game(n, devil=False):
2
3 score_dict = {}
4 print('Game on... (%i rounds in all)' % n)
5 if devil == False:
6 end = 12
7 else:
8 end = 13
9
10 scores = [0 for i in range(end)]
11
12 for i in range(1, end):
13 for j in range(i+1, end):
14 me_score, him_score = game_dual(n,i,j)
15 score_dict[i,j] = [me_score,him_score]
16 print('%i v.s. %i: %i – %i' % (i,j,me_score,him_score))
17 scores[i] += me_score
18 scores[j] += him_score
19 outputF.write('devil = %s and n = %i\n' % (str(devil), n))
20 outputF2.write('*** devil = %s and n = %i ***\n' % (str(devil), n))
21
22 for i in range(1,end): # total scores of each one
23 outputF.write('%i,%i\n' % (i, scores[i]))
24 print('%i: %i' % (i, scores[i]))
接着,为了分析最佳应答策略,我分析比赛结果score_dict
,把每个人从score_individual[i]
里。
xxxxxxxxxx
111 scores_individuals = {}
2 for i in range(1,end):
3 scores_individuals[i] = {} # store how many points each one get from i
4
5 for key,value in score_dict.items():
6 for i in range(1,end):
7 if i in key:
8 i_index = key.index(i)
9 j = key[1 - i_index]
10 j_score = value[1 - i_index]
11 scores_individuals[i][j] = j_score
从
xxxxxxxxxx
61 best_reacted_dict = {}
2 for key, value in scores_individuals.items():
3 best_reacted = [0,0] # [j, j_score] (j reacted best against key, with a score of j_score)
4 for j, j_score in value:
5 if j_score > best_reacted[1]:
6 best_reacted = [j, j_score]
考虑有多个最佳应答策略的情况,他们从
xxxxxxxxxx
31 elif j_score == best_reacted[1]:
2 best_reacted.append(j)
3 best_reacted.append(j_score)
考虑不存在最佳应答策略的情况,删除用来占空的[0,0]
。
xxxxxxxxxx
21 if best_reacted[0:2] == [0,0]:
2 del best_reacted[0:2]
输出分析结果
xxxxxxxxxx
41 best_reacted_dict[key] = best_reacted
2 print('* 对%i,p%s是最佳应答策略。' % (key, best_reacted[0:-1:2]))
3 outputF2.write('* 对%i,p%s是最佳应答策略。\n' % (key, best_reacted[0:-1:2]))
4 print()
非严格占优策略dominant
对于所有玩家都是最佳占优策略。
xxxxxxxxxx
41 dominant = [1] + best_reacted_dict[1][0:-1:2]
2 for i in range(2, end):
3 next_range = best_reacted_dict[i][0:-1:2] + [i]
4 dominant = [x for x in dominant if x in next_range]
两个人互相是对方的最佳占优策略,也值得分析。
xxxxxxxxxx
71 reciprocal = []
2 for key, value in best_reacted_dict.items():
3 for j in range(0, len(value), 2):
4 if value[j] > key:
5 for k in range(0, len(best_reacted_dict[value[j]]), 2):
6 if key == best_reacted_dict[value[j]][k]:
7 reciprocal.append([key,value[j]])
为了方便重复调用,以上分析都被包含在game
函数中。game
函数最后返回score_dict
。
xxxxxxxxxx
11 return score_dict
以下是主程序。循环次数不同时、有无devil时,计算比赛的不同结果。
xxxxxxxxxx
71score_dict_whole = {0:{}, 1:{}}
2
3for n in [1,10,100,1000]:
4 print('无devil, n = %i' % n)
5 score_dict_whole[0][n] = game(n)
6 print('有devil, n = %i' % n)
7 score_dict_whole[1][n] = game(n, True)
n=1000, devil=True
时,输出结果如下:
xxxxxxxxxx
151>>> *** devil = True and n = 1000 ***
2>>> * 对1,p[10]是最佳应答策略。
3>>> * 对2,p[1, 3, 4, 5, 6, 7, 8, 9, 11]是最佳应答策略。
4>>> * 对3,p[1, 2, 4, 5, 6, 7, 8, 9, 11]是最佳应答策略。
5>>> * 对4,p[1, 2, 3, 5, 6, 7, 8, 9, 11]是最佳应答策略。
6>>> * 对5,p[1, 2, 3, 4, 6, 7, 8, 9, 10, 11]是最佳应答策略。
7>>> * 对6,p[1, 2, 3, 4, 5, 7, 8, 9, 11]是最佳应答策略。
8>>> * 对7,p[1, 2, 3, 4, 5, 6, 8, 9, 11]是最佳应答策略。
9>>> * 对8,p[1, 2, 3, 4, 5, 6, 7, 9, 11]是最佳应答策略。
10>>> * 对9,p[1, 2, 3, 4, 5, 6, 7, 8, 11]是最佳应答策略。
11>>> * 对10,p[12]是最佳应答策略。
12>>> * 对11,p[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]是最佳应答策略。
13>>> * 对12,p[5]是最佳应答策略。
14>>> (非严格)占优策略:无。
15>>> 互为最佳应答策略:2 – 3 | 2 – 4 | 2 – 5 | 2 – 6 | 2 – 7 | 2 – 8 | 2 – 9 | 2 – 11 | 3 – 4 | 3 – 5 | 3 – 6 | 3 – 7 | 3 – 8 | 3 – 9 | 3 – 11 | 4 – 5 | 4 – 6 | 4 – 7 | 4 – 8 | 4 – 9 | 4 – 11 | 5 – 6 | 5 – 7 | 5 – 8 | 5 – 9 | 5 – 11 | 6 – 7 | 6 – 8 | 6 – 9 | 6 – 11 | 7 – 8 | 7 – 9 | 7 – 11 | 8 – 9 | 8 – 11 | 9 – 11 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | devil |
---|---|---|---|---|---|---|---|---|---|---|---|
69100 | 68918 | 69136 | 75545 | 71928 | 68622 | 69136 | 69129 | 69136 | 61366 | 68695 | 27003 |
我的策略函数(编号10)的分数并不好,在p4与devil面前失分最多。
对于p4和devil以外的策略,我丢分的原因在于,我设置的“一报还一报”的比例门槛是0.9,这就意味着,当别人并没有完全按照一报还一报的策略进行时,我仍然在选择合作,这损伤了我的分数。