zoukankan      html  css  js  c++  java
  • Game theory

    Game theory初步

    游戏1
    l    有两个游戏者:A和B。
    l    有21颗石子。
    l    两人轮流取走石子,每次可取1、2或3颗。
    l    A先取。
    l    取走最后一颗石子的人获胜,即没有石子可取的人算输。
    如果剩下1、2或3颗石子,那么接下来取的人就能获胜;如果剩下4颗,那么无论接下来的人怎么取,都会出现前面这种情况,所以接下来取的人一定会输;如果剩下5、6或7颗石子,那么接下来取的人只要使得剩下4颗石子,他就能获胜。0,4,8,12,……都是下一个取石子者的必败状态。现在有21颗石子,21除以4的余数是1,所以先走者有必胜的策略,他第一次只要取走1颗石子,以后每一次都保证剩下的石子是4的倍数就行了。

    什么是“平等组合游戏”?
    l    两人游戏。
    l    有一个状态集,而且通常是有限的。
    l    规定哪些状态转移是允许的。
    l    所有规定对于两人来说是一样的。
    l    两人轮流走步。
    l    有一个终止状态,到达终止状态后游戏即告终止。
    l    游戏可以在有限步内终止。

    P状态和N状态
    就像第一个游戏一样,状态0,4,8,……是刚才走步的人的必胜状态,我们称之为P状态;而1,2,3,5,6,7,……都是下一个走步的人的必胜状态,我们称之为N状态。
    我们可以从终止状态出发,推出每一个状态,指出它是P状态还是N状态。就拿第一个游戏举例:
    步骤一 将所有终止状态设为P状态。
    步骤二 将所有一步之内可以到达一个P状态的状态设为N状态。
    步骤三 如果一个状态,不管怎么走都只能走到N状态,那么就将这个状态设为P状态。
    步骤四 返回步骤二。
    如果能够走到P状态,就能获胜。因为安照上面的定义,对手不管如何选择,只可能走到N状态。接下来总存在一个P状态你可以走到。这样一直走到终止状态,你获胜。当然这里所说得都是指对于最后走步的人获胜的游戏。

    我们严格的来定义P状态和N状态
    l    所有的终止状态都是P状态;
    l    对于任何的N状态,肯定存在一种方式可以一步转到一个P状态;
    l    对于任何的P状态,不管怎么走步,都只能转到N状态。
    而对于最后走步的人失败的游戏,只要将所有终止状态改成N状态,然后开始倒推就可以了。当然,必胜状态是N状态。也就是说,如果想胜利,就希望面对N状态,转移到P状态。

    现在对游戏1略微扩展一下。
    有一个决策集S,S中的元素是正整数。游戏的规则大致与游戏1一样,只是现在每次可以取的石子数必须是S中的元素。如果S={1,2,3},那么就是游戏1。
    大家分析一下,当S={1,3,4}的时候,哪些状态是P状态,哪些是N状态。
    我们发现P状态是{0,2,7,9,14,16,……},N状态是{1,3,4,5,6,8,10,……}。
    规律是如果n除以7的余数是0或2,那么状态n就是P状态,否则就是N状态。
    如果游戏开始时,石子总数是100,那么这是一个P状态,也就是说后走的人有必胜策略。


    游戏2 Nim游戏
    有三堆石子,分别含有x1,x2和x3颗石子。两人轮流取石子,每次可以选择一堆,从这堆里取走任意多颗石子,但不能不取。取走最后一颗石子的人获胜。

    我们用三元组来表示状态,很明显(0, 0, 0)是唯一的终止状态,是P状态。
    先考虑只剩一堆有石子的情况(0, 0, x),很明显这是,这些状态都是N状态。
    剩两堆的情况,如果两堆的石子数相等(0, x, x),那么这些都是P状态。因为下一次走步的人一定会使得两堆石子不相等,再下一次可以使得两堆的石子数回到相等的状态,包括终止状态。如果两堆的石子数不相等,那么就是N状态。
    三堆都非空的情况就复杂得多。我们可以得到(1, 1, 1)、(1, 1, 2)、(1, 1, 3)和(1, 2, 2)都是N状态,因为它们可以转变成(0, 1, 1)或(0, 2, 2),它们都是P状态。(1, 2, 3)是P状态,因为不管怎么选择,下一次一定变到N状态。

    “Nim和”就是两个数二进制表示的不进位加法,也就是两个整数进行xor位运算。
    定义:两个数(xm…x0)2和(ym…y0)2,是(zm…z0)2,其中zi=(xi+yi) mod 2,0<=i<=m。
    例如,22和51的Nim和是37:


    整数关于Nim和(以后用“+”表示)满足交换律和结合律。有单位元0,因为0+x=x。任何两个相等的数之和是0,即x+x=0。有削去律,即如果x+y=x+z,那么y=z。因为,如果x+y=x+z,两边都加上x,得到x+x+y=x+x+z,即y=z。

    定理1:Nim游戏的一个状态(x1, x2, x3) 是P状态,当且仅当x1+x2+x3=0。

    考虑状态(13, 12, 8)。Nim和是9,不等于0,所以这是一个N状态。

    那么接下来应该怎么走,才能走到一个P状态呢?你可以从第一堆中取走9颗石子。

    或者你也可以从第二堆中取走7颗石子,等等。

    如果石子的堆数大于3,只要堆数是有限的,上面的定理仍然成立。即如果有n堆石子,状态(x1, x2, …, xn)是P状态的充要条件是x1+x2+…+xn=0。下面就来证明。
    我们用ρ表示所有Nim和为零的状态组成的集合;用п表示ρ的补集,即所有Nim和为正整数的状态组成的集合。让我们逐一检验P状态和N状态的定义。
    l    所有的终止状态都在ρ中。由于终止状态只有一个(0, 0, …, 0),0+0+…+0=0。
    l    所有属于п的状态,一步之内一定可以走到ρ中的状态。找出Nim和最左端为1的那一列,然后任意选择一个这一列是1的堆,从这堆中取走若干颗石子,使得Nim和为0。这总是可以做到的,因为将那一列的1变成0,而它左边的列不用修改,这个数就肯定变小了。对于其他Nim和是1的列,只要将这个数相对列的0改成1,1改成0就可以了。
    l    所有属于ρ的状态,一定转变到п中的状态。任意一个P状态(x1, x2, …, xn),不妨假设从第一堆中取出若干颗石子。如果存在x1’<x1,而(x1’, x2, …, xn)也是P状态。那么x1+x2+…+xn=0=x1’+x2+…+xn,根据前面讲的削去律,x1’=x1,与假设x1’<x1矛盾。所以(x1’, x2, …, xn)一定是N状态,属于п。

    通过上面的证明,你能得到从一个N状态走到P状态的方案数吗?而且这个数是奇数。

    那么,对于最后走步的人失败的Nim游戏,又怎么办呢?通常情况下,这类游戏比最后走步的人获胜的游戏难得多。但Nim游戏是个例外。我们来分析一下。
    P状态和N状态的定义不变,如果初始状态是N状态,先走者有必胜策略。当超过1颗石子的堆数大于1的时候,按照前面所讲的方法走。直到超过1颗石子的堆数等于1,这时将这堆石子全部取掉或剩1颗,保证非空(剩下1颗石子)的堆数为奇数。如果初始状态是N状态,按照策略,先走者不可能将“超过1颗石子的堆数等于1”的状态留给对方,因为这样的状态不可能是P状态。而且对方不可能在一步之内从“超过1颗石子的堆数大于1”的状态变到“超过1颗石子的堆数小于1”的状态。


    图游戏
    现在我们使用有向图来描述一个游戏,所有的状态用顶点表示,所有合法的移动用有向边表示。接下来我们会给出Sprague-Grundy函数(简称SG函数),它比起P状态和N状态,能够提供更多的信息。

    定义:用(X, F)来表示有向图G。X是顶点集,F是后继函数。设x是一个顶点,F(x)是一个集合,包含于X,任意一个元素y属于F(x),表示从x出发到y有一条边。F(x)就是x的后继集合,也可看成从x出发的决策集。如果F(x)是空集,那么就表示x是终止状态。

    图游戏:一个两人游戏,在一个图G(X, F)上玩,指明一个顶点x0并按照下列的规则:
    l    A先走,从x0开始;
    l    两人轮流走步;
    l    从顶点x出发,只能走到顶点y,y属于F(x);
    l    遇到终止状态,即不能走步,此人输。

    对于一个图,如果不管x0是哪个点,总存在一个n,使得从x0出发的任意一条路经的长度都不超过n,那么这个图就被称为是“递增有界”的。接下来主要讨论递增有界的图游戏。
    拿游戏1来举例,设有n颗石子。顶点集X={0, 1, 2, …, n},F(0)是空集,F(1)={0},F(2)={0, 1},F(k)={k-3, k-2, k-1},3<=k<=n。下图是n=10的情况。



    SG函数
    定义:
    对于一个递增有界的图G(X, F)来说,SG函数g,是定义在X上的函数,函数值是非负整数,使得


    用语言来描述就是:g(x)的值等于所有x的后继的SG函数中没有出现的最小非负整数。
    对于递增有界的图,SG函数是唯一的、有界的。
    所有的终止状态x,因为F(x)是空集,所以g(x)=0。

    给出下图的SG函数。


    例1
    给出游戏1的SG函数,看看有什么规律,与P状态和N状态有什么关系。
    x    0    1    2    3    4    5    6    7    8    9    10    11    …
    g(x)    0    1    2    3    0    1    2    3    0    1    2    3    …

    例2
    有一堆石子,设当前剩下n颗石子,这一步至少要取走n/2取上界颗。唯一的终止状态是剩0颗石子。给出SG函数,看看有什么规律。
    x    0    1    2    3    4    5    6    7    8    9    10    11    12    …
    g(x)    0    1    2    2    3    3    3    3    4    4    4    4    4    …

    根据例1的结果,我们猜测SG函数与P状态和N状态是有关的。如果g(x)=0,那么x就是P状态,否则x就是N状态。证明是很显然的,我们只要根据两者的定义,考虑以下三点:
    l    如果x是终止状态,那么g(x)=0。
    l    一个状态x,如果g(x)≠0,那么一定存在一个x的后继y,使得g(y)=0。
    l    一个状态x,如果g(x)=0,那么所有x的后继y,都有g(y)≠0。
    当然,SG函数还包含了其他的信息,这些信息在以后会用到。


    多个组合游戏的并
    给定若干个组合游戏,可以按照下面的规则将它们并成一个新的游戏。
    l    对每个游戏给定初始状态。
    l    两人轮流走步,从A开始。
    l    每一轮,选择一个未到达终止状态的游戏,在这个游戏中按照规则走一步,其他游戏的状态不变。
    l    最后一个走步者获胜,即走完之后所有游戏都到达终止状态。
    我们称这个新的游戏为“多个组合游戏的并”。我们要来看如何用每一个游戏的SG函数来求这个新的组合游戏的SG函数。

    n个图游戏的并
    定义:有n个递增有界的图游戏G1(X1, F1),……,Gn(Xn, Fn)。把它们合并成一个新的游戏G(X, F),记为G=G1+G2+…+Gn。X是所有游戏顶点集的笛卡尔积,即X=X1*X2*…*Xn。也就是说,我们用n元组(x1, x2, …, xn)来表示G中的顶点x,其中xi属于Xi,对于所有的i。x的后继F(x)可以定义成:

    这样定义的新的游戏G,一定也是递增有界的。把每个游戏的界相加,就得到了新游戏的界。
    正如Nim游戏那样,如果堆数是1,那么非常简单;如果堆数是2,也很容易分析;但堆数如果大于2,就不是很明显了。所以即使每个图游戏都是很平凡的,n个图游戏的并也可能相当复杂。

    下面介绍的SG定理可以看成是定理1的一般化。
    定理2
    设G=G1+G2+…+Gn,Gi的SG函数是gi,i=1, 2, …, n。那么G的SG函数g(x1, x2, …, xn)=g1(x1)+g2(x2)+…+gn(xn),加法表示Nim和,即不进位的二进制加法。
    证明:
    令x(x1, x2, …, xn)是X中任意一点,b= g1(x1)+g2(x2)+…+gn(xn)。
    根据SG函数的定义,我们要说明两点:
    (1)、对于任意的非负整数a(a<b),一定存在一个x的后继y,使得g(y)=a。
    (2)、x的任意一个后继y,都有g(y)¹b。
    首先来说明(1)。设d=a+b(nim和),d的二进制表示有k位,则2k-1<=d<2k。d的第k位是1而且a<b,所以a的第k位是0,b的第k位是1。因为b= g1(x1)+g2(x2)+…+gn(xn),所以至少存在一个分量的第k位是1,不妨设它就是g1(x1)。那么,就有d+g1(x1)<g1(x1),也就存在从x1到x1’的一次走步,使得g1(x1’) =d+g1(x1)。那么g1(x1’)+g2(x2)+…+gn(xn)=d+g1(x1)+g2(x2)+…+gn(xn) = d+b=a。
    再说明(2)。反证法。不失一般性,假设后继的走步是从x1到x1’,又有g1(x1’)+g2(x2)+…+gn(xn) =g1(x1)+g2(x2)+…+gn(xn)。根据消去率,g1(x1’)=g1(x1),这与SG函数的定义不符,假设不成立。

    例3、你每次可以从一堆石子中取走{1, 2, …, m}颗。对于1堆的问题,SG函数gm(x)=x mod (m+1)。如果考虑3个这样的游戏的并,第一个游戏m=3,有9颗石子;第二个游戏m=5,有10颗石子;第三个游戏m=7,有14颗石子。g(9,10,14)=g3(9)+g5(10)+g7(14)=1+4+6=3,是一个N状态。要取胜的话,下一次可以选择第三个游戏,取走1颗石子,使得g7(13)=5。那么,还有别的取法吗?
    var
      n,m,p,t,i:longint;

    begin
      readln(n);
      t:=0;
      for i:=1 to n do begin
        readln(m,p);
        t:=t xor (p mod (m+1));
        end;
      if t=0 then writeln('P') else writeln('N');
    end.


    取走-分割游戏
    这种游戏允许取走某些东西,然后将原来的一个游戏分成若干个相同的游戏。
    例1、Lasker’s Nim游戏:每一轮允许两种操作之一。(1)从一堆石子中取走任意多个(2)将一堆数量不少于2的石子分成都不为空的两堆。
    分析:
    很明显,g(0)=0,g(1)=1。状态2的后继有0,1和(1,1),它们的SG函数值分别是0,1和0,所以g(2)=2。状态3的后继有0,1,2和(1,2),它们的SG函数值分别是0,1,2和3,所以g(3)=4。状态4的后继有0,1,2,3,(1,3)和(2,2),它们的SG函数值分别是0,1,2,4,5和0,所以g(4)=3。在推一些,我们得到:

    我们推测:对于所有的k>=0,有g(4k+1)=4k+1;g(4k+2)=4k+2;g(4k+3)=4k+4;g(4k+4)=4k+3。
    请自行证明。
    假设游戏初始时有3堆,分别有2、5和7颗石子。三堆的SG函数值分别是2、5和8,它们的Nim和等于15。所以要走到P状态,就要使得第三堆的SG值变成7,可以将第三堆分成按1和6分成两堆。
    var
      g:array[0..100] of longint;
      b:array[0..100] of boolean;
      n,i,j:longint;

    begin
      readln(n);
      g[0]:=0;
      for i:=1 to n do begin
        fillchar(b,sizeof(b),false);
        for j:=0 to i-1 do b[g[j]]:=true;
        for j:=1 to i div 2 do b[g[j] xor g[i-j]]:=true;
        j:=0;
        while b[j] do inc(j);
        g[i]:=j;
        writeln(i,': ',j);
        end;
    end.


    PAS
    2人玩的游戏,一个p*1的棋盘和红、绿、蓝三种棋子,棋子的大小分别是c*1、z*1和n*1,每种颜色的棋子个数无限。两人轮流摆放棋子,规则是:
    棋子不得超出棋盘范围;
    棋子不能有任何部分重叠;
    如果哪个人没有棋子可放,即算输。
    判断先手是否有必胜策略。
    输入:
    第一行是正整数c、z和n,都不超过1000。
    第二行是m,表示棋盘种类。接下来的m行,每行一个正整数p。m和p都不超过1000。
    输出:
    对于每种棋盘输出一行,如果先手必胜输出1,否则输出2。
    样例
    输入
    1 5 1



    6
    输出
    1
    1
    2

    var
      c:array[1..3] of longint;
      g,q:array[0..1000] of longint;
      b:array[0..1000] of boolean;
      m,maxq,i,j,k:longint;

    begin
      assign(input,'pas.in'); reset(input);
      assign(output,'pas.out'); rewrite(output);
      readln(c[1],c[2],c[3]);
      readln(m);
      maxq:=0;
      for i:=1 to m do begin
        read(q[i]);
        if q[i]>maxq then maxq:=q[i];
        end;
      g[0]:=0;
      for i:=1 to maxq do begin
        fillchar(b,sizeof(b),false);
        for j:=1 to 3 do
          for k:=0 to i-c[j] do b[g[k] xor g[i-c[j]-k]]:=true;
        j:=0;
        while b[j] do inc(j);
        g[i]:=j;
        end;
      for i:=1 to m do
        if g[q[i]]=0 then writeln(2) else writeln(1);
      close(input); close(output);
    end.
  • 相关阅读:
    常用知识点集合
    LeetCode 66 Plus One
    LeetCode 88 Merge Sorted Array
    LeetCode 27 Remove Element
    LeetCode 26 Remove Duplicates from Sorted Array
    LeetCode 448 Find All Numbers Disappeared in an Array
    LeetCode 219 Contains Duplicate II
    LeetCode 118 Pascal's Triangle
    LeetCode 119 Pascal's Triangle II
    LeetCode 1 Two Sum
  • 原文地址:https://www.cnblogs.com/Aragaki/p/7149201.html
Copyright © 2011-2022 走看看