今天初步学习了一下博弈论……感觉真的是好精妙啊……希望这篇博客可以帮助到和我一样刚学习博弈论的同学们。
博弈论,又被称为对策论,被用于考虑游戏中个体的预测行为和实际行为,并研究他们的应用策略。(其实这句话没有什么卯月)
在OI中,博弈论的主要应用是一些经典的模型,以及sg函数,sj定理的应用。
首先我们来看博弈论最为经典的模型之一:Nim游戏
有n堆石子,每次可以从其中任意一堆石子中取出若干块石子(可以取完),不能不取。
最后无石子可取者为输家。假设两人都按最优情况走,问是否先手必胜。
为了计算这个问题,我们对这些状态定义position,设N-position(now-position)表示先手必胜状态,P-position(pre-position)表示先手必败状态
我们再定义某状态的后继状态为这个状态取走一些石子之后达到的新状态
那么显然,N-position的后继状态一定有一个是P-position,而P-position的后继状态全是N-position
有了这两种定义,我们再来观察Nim游戏:显然,0枚石子是一个P-position,接着,1,2,……inf枚石子都是N-position
而对于两堆,三堆甚至更多的情况,我们只需要考察后继状态是否全为N-position,这样我们就找到了一个计算某个状态是否为必胜状态的方法,利用记忆化搜索判断即可。但是,如果设有n堆石子,他们的个数分别是a1,a2........an,那么搜索的状态数是a1*a2*.....*an种,显然当数据变大的时候这个较为暴力的算法就不优秀了
因此,接下来我们引入一个新的函数来协助我们的运算:sg函数。sg函数,是一种代表游戏局面的函数,其值为0时该状态是必败状态,反之,>0是必胜状态。
在一开始,我们并不知道对于某个游戏的sg()的计算方式是什么,我们只能通过手玩小数据->猜测计算方式->回带验证的方式来计算一个sg()的计算方式
但这些并没有什么卯月仍然没有解决上面Nim游戏中状态过多的问题,因此为了解决它,现在我们先来说结论,下面给出证明:这个状态的#S,等于它所有子游戏T1,T2,....Tn的#T值异或和,即#S=#T1^#T2^....^#Tn
局面为#S>0时,设#S=a1^a2^…^an=d,假设d的二进制最高位为k,那么一定存在至少一个ai第k位为1,显然ai^d<ai。(因为异或后第k位变为0,ai减去2^k,而其它位最多加上2^k-1.)那么只要将ai改变为ai^d,异或和变为d^a1^…^an=d^d=0。即#S>0时一定可以走到一个#T=0的状态。
当游戏为终止局面#S=0时,假设可以将ai变为ai‘,使#T为0.由于#S=a1^…^ai^…^an=0,即ai=a1^…^ai-1^ai+1^…^an。同理得ai’= ai=a1^…^ai-1^ai+1^…^an。这样ai=ai‘,显然矛盾,所以一个#S=0的状态只能到达#T>0。
在有了这个异或操作之后,我们就可以在O(n)时间内求出目标游戏状态的sg值了,只需要把它的子游戏异或起来即可。但注意,异或不是所有的游戏都适用。首先,只有当游戏步数有限,不能移动者输,并且双方公平游戏的时候才能使用。其次,异或只能用于处理子游戏状态的加和。如果某一个状态已经不能继续拆成子游戏了(比如Nim游戏的一堆石子),就不能用异或来求解sg值了,正因为异或有局限性,所以我们需要掌握真正推导sg函数的方法。我们假设已经设出一个sg函数,考虑如何验证。
不难看出,在设某个多堆的石子状态为S,其sg值为#S,其后继状态为T的情况下,使得:若#S=0,则所有#T>0;若#S>0,则存在#T=0。如果这两个状态成立,则刚才规定的sg计算方法是正确的。那么如何求一个正确的sg函数值呢?
为了方便证明,我们设g(S)为状态S的后继状态T的#T的集合。
还用nim游戏的例子来证明,我们设S的n堆石子为a1~an。那么如果#S=0,则#S=sg(a1)^sg(a2)^sg(a3)......^sg(an)=0.我们设这一步的策略是把ai改为ai',则有
sg(ai)=sg(a1)^sg(a2)^sg(a3)..^sg(i-1)^sg(i+1)^...^sg(an),接着#S'=sg(a1)^sg(a2)^sg(a3)....^sg(ai')..^sg(an)=sg(ai')^sg(ai)
显然,ai不等于ai',即#S'>0,命题得证!
如果#S=sg(a1)^sg(a2)^sg(a3)......^sg(an)>0,还是设这一步的策略是把ai改为ai',则有
那么从刚才Nim游戏的证明可以知道,ai'取ai'^#S时为最优策略,这时#T=#S^#S=0.由于ai'^#S<ai并且ai^S#的值并不确定,因此0~sg(a[i])-1属于g(S)
现在,再看看我们得到了什么结论:sg(ai)∉g(S),并且sg(0)~sg(ai-1)∈g(S)。那么,显然这个sg(ai)就是g(S)中最小的未出现自然数(mex操作)啊!
这样的话,我们就可以递推的求sg函数值了。mex的查询可以O(n)扫,也可以O(log2n)用树状数组维护。并且,现在我们就可以证明一些前面证明不了的东西了:比如Nim游戏的sg值为什么是sg(x)=x
证明:显然sg(0)=0,那么sg(1)=mex(sg(0))=mex(0)=1,sg(2)=mex(sg(1),sg(0))=mex(1,0)=2........那么这样就可以看出sg(x)=x了
其他的sg函数值也可以这样算,但一般效率不太高,需要自己定义适合题意的sg函数快捷计算方法,并且证明。
sg函数的基础内容就是这些,接下来还会有sg函数知识的进阶和一些基础题目讲解。今天就到这里:)