博弈均衡算法

来源:互联网 发布:新生分班软件 编辑:程序博客网 时间:2024/05/17 21:56
博弈
(一).理解

博弈均衡是指使博弈各方实现各自认为的最大效用,即实现各方对博弈结果的满意,使各方实际得到的效用和满意

程度是不同的。在博弈均衡中,所有参与者都不想改变自己的策略的这样一种相对静止的状态。

非合作博弈理论中最基本的均衡概念就是纳什均衡,它只能描述均衡点的局部静态性质;进化博弈理论基本均衡

念就是进化稳定策略,它是也是一个静态概念,但可以描述系统的局部动态性质;进化博弈理论另一个重要概念

是随机稳定状态,它是一个动态概念,能够描述系统的全局动态性质。

与前两者不同,随机稳定状态并不是不动的,它只能描述系统的一种长期行为,从长期来看,系统在随机因素影响

大多数时间都处于某个均衡,下面分别阐述三个概念:

①:所谓纳什均衡策略是一个策略组合,是指在其他参与人选择一定的条件下,每一个参与人都选择获得最大支

付的策略,换句话说,纳什均衡状态就是任何单独偏离不会得到改善的一种状态。

②:进化稳定策略。首先,我们可以看出所有进化策略都是纳什均衡策略,所进化稳定策略集是纳什均衡策略集

的子集,非纳什均衡略就不是进化稳定策略;其次,由定义中的两个条件可以得出,稳定策略者与稳定策略者群

博弈时,突变策略者不会比稳定策略者好;如果突变策略者与稳定策略者一样好,那么,突变者策略与突变策略

群体博弈时,就没有稳定策略者与突变策略群体博弈时好。也就是说,稳定策略具有对少数突变者的免疫力,在稳

定状态时,突变者是不容易侵入的。

计算进化稳定策略的方法主要有两大类:一是从动态过程出发,求出系统的平衡点,然后,再根据进化稳定策略

的定义进行验证就可以了;另一种方法就是直接用进化稳定策略定义来求。第一种方法涉及到具体的动态过程,并

只要知道动态过程就很容易求出进化稳定策略。第二种方法就是通过定义来求,下面给出一种简单的处理方法:

根据纳什均衡的定义可以知道,如果策略 是博弈的纳什均衡,那么,所有以正概率进入最优混合策略的纯策略都

是最优的,参与人在所有这些纯策略所得的支付都是无差异的。

如果某策略组合是严格纳什均衡策略,那么就可以直接得出它就是进化稳定策略,但如果是弱纳什均衡策略,那

么就可运用上述的方法来进行判定。由此,可得到求博弈的进化稳定策略步骤:一是求出博弈所有的纳什均衡;二

由支付判断出其中的严格纳什均衡;三对非严格纳什均衡而言就代入上述方程,并判断是否为负定即可以求出博

弈中所有进化稳定策略。

③:随机稳定状态是描述系统长期行为且由概率来定义的。如果系统是连续情形,那么可根据Foster and Young

(1990)通过求系统随机潜力的方法来求随机稳定状态,即有最小随机潜力的状态就是随机稳定状态。而现实中,多

情况都是离散的,下面将根据Freidlin, M. I and Wentzell, A . D. (1984)的方法来给出有多个常返状态情形下随机

潜力的计算方法。该方法首先要求每个参与人在任何状态任何时候都以相同且不为零的突变率选择其他任何策略,

这样就可以保证系统的遍历性,从而存在平稳分布。

(二).总结博弈题目

①.巴什博奕(Bash Game):只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个

最后取光者得胜。

显然,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余

的物品,后者取胜。因此我们发现了如何取胜的法则:如果n=(m+1)* r+s,(r为任意自然数,s≤m),那么先

者要拿走s个物品,如果后取者拿走k(k≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,

以后持这样的取法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。

这个游戏还可以有一种变相的玩法:两个人轮流报数,每次至少报一个,最多报十个,谁能报到100者胜。

总结:通过分析可知,设n=(m+1)*r+s(s<=m),如果s等于0,则后取者获胜!如果s不等于0,则先取者胜利!

②.威佐夫博奕(Wythoff Game):有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,

定每次至少取一个,多者不限,最后取光者得胜。

这种情况下是颇为复杂的。我们用(ak,bk)(ak ≤ bk ,k=0,1,2,……,n)表示两堆物品的数量并称其为

势,如果甲面对(0,0),那么甲已经输了,这种局势我们称为奇异局势。前几个奇异局势是:(0,0)、

(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。

我们可以知道,后面的奇异局势可以通过一轮特殊的取法变为更低的奇异局势!

可以看出,a0=b0=0,ak是未在前面出现过的最小自然数,而 bk= ak + k,奇异局势有如下三条性质:

1. 任何自然数都包含在一个且仅有一个奇异局势中。

由于ak是未在前面出现过的最小自然数,所以有ak > ak-1 ,而 
bk= ak + k > ak-1 + k-1 = bk-1 > ak-1 .所以性质1.成立。

2. 任意操作都可将奇异局势变为非奇异局势。

事实上,若只改变奇异局势(ak,bk)的某一个分量,那么另一个分量不可能在其他奇异局势中,所以必然是非

奇异局势。如果使(ak,bk)的两个分量同时减少,则由于其差不变,且不可能是其他奇异局势的差,因此也是

非奇异局势。

3. 采用适当的方法,可以将非奇异局势变为奇异局势。

假设面对的局势是(a,b),若 b = a,则同时从两堆中取走 a 个物体,就变为了奇异局势(0,0);

如果a = ak ,b > bk,那么,取走b - bk个物体,即变为奇异局势;如果 a = ak , b < bk ,则同时从两堆中拿走

ak - ab - ak个物体,变为奇异局势( ab - ak , ab - ak+ b - ak);如果a > ak ,b= ak + k,则从第一堆中拿走多余

的数量a - ak 即可;如果a < ak ,b= ak + k,分两种情况,第一种,a=aj (j < k),从第二堆里面拿走 b - bj 即可;

第二种,a=bj (j < k),从第二堆里面拿走 b - aj 即可。

从如上性质可知,两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。

那么任给一个局势(a,b),怎样判断它是不是奇异局势呢?我们有如下公式:
ak =[k*(1+√5)/2],bk= ak + k (k=0,1,2,……,n 方括号表示取整函数)

奇妙的是其中出现了黄金分割数(1+√5)/2 = 1.618……,因此,由ak,bk组成的矩形近似为黄金矩形,

由于2/(1+√5)=(√5-1)/2,可以先求出j=[a(√5-1)/2],若a=[j*(1+√5)/2],那么a = aj,bj = aj + j,

若不等于,那么a = aj+1,bj+1 = aj+1+ j + 1,若都不是,那么就不是奇异局势。然后再按照上述法则进行,一定

会遇到奇异局势。

总结:分析到此已经很明显,判断谁个能够胜利看是否是奇异矩阵就行了,如果是奇异矩阵,则后取者胜利,

如果是非奇异矩阵,则先取者得胜!在判断是不是咸佐夫博弈的奇异局势的时候,比如两个数a,b,则可以首先交

换是a<b,然后记i=b-a,如果是奇异局势,则必有m=floor(i*(1+sqrt(5.0))/2),并且b=m+i,否则比不是奇异局势! 

③.尼姆博奕(Nimm Game):有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取

一个,多者不限,最后取光者得胜。

这种情况最有意思,它与二进制有密切关系,我们用(a,b,c)表示某种局势,首先(0,0,0)显然是奇异局

势,无论谁面对奇异局势,都必然失败。第二种奇异局势是(0,n,n),只要与对手拿走一样多的物品,最后都

将导致(0,0,0)。仔细分析一下,(1,2,3)也是奇异局势,无论对手如何拿,接下来都可以变为

(0,n,n)的情形。

计算机算法里面有一种叫做按位模2加,也叫做异或的运算,我们用符号(+)表示这种运算。这种运算和一般加

法不同的一点是1+1=0。先看(1,2,3)的按位模2加的结果:

1 =二进制01 

2 =二进制10 

3 =二进制11 

(+)———

0 =二进制00 (注意不进位)

对于奇异局势(0,n,n)也一样,结果也是0。

任何奇异局势(a,b,c)都有a(+)b(+)c =0。

如果我们面对的是一个非奇异局势(a,b,c),要如何变为奇异局势呢?假设 a < b< c,我们只要将 c 变为

 a(+)b,即可,

因为有如下的运算结果: a(+)b(+)(a(+)b)=(a(+)a)(+)(b(+)b)=0(+)0=0。

要将c 变为a(+)b,只要从 c中减去 c-(a(+)b)即可。

总结:面对奇异局势,先取者必定输,如果是非奇异局势,则先取者赢!如果一个局势(a,b,c有a(+)b(+)c==0,

则是奇异局势,如果不是,则不是奇异局势!三堆石子的结论同样也适用于n堆石子的情况!

(三).例题

(①).巴什博弈https://vjudge.net/contest/173215#problem/G

#include <stdio.h>
int main()
{
    int c ;
    scanf("%d",&c);
    while(c--)
    {
        int n , m ;
        scanf("%d%d",&n,&m) ;
        if(n%(m+1) == 0)//只需要这个公式;
        {
            puts("Rabbit") ;
        }
        else
        {
            puts("Grass") ;
        }
    }
    return 0 ;
}

题解:掌握思路,记住这个公式。

(②).威佐夫博弈https://vjudge.net/contest/173215#problem/F

代码:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
int main()
{
    int a, b, m, k, t;
    while (~scanf("%d%d", &a, &b))
    {
        if (a > b)
        {
            t = a;
            a = b;
            b = t;
        }
        k = b - a;
        m = (int) ((sqrt(5.0) + 1.0) * k / 2);
        printf("%d\n", m == a ? 0 : 1);
    }
    return 0;
}

题解:

首先判断是否是奇异局势,

先是进行判断交换使a<b,然后k=b-a;若是奇异局势m= (int) ((sqrt(5.0) + 1.0) * k / 2);

在判断a是否=m就可以得出结果。

(③).NIM博弈 https://vjudge.net/contest/173215#problem/H

代码:

#include<stdio.h>  
long long a[2000];  
int n;  
int main()  
{  
    while (scanf("%d", &n) && n)  
    {  
        int s = 0, ans = 0;  
        for (int i = 0; i < n; i++)  
        {  
            scanf("%lld", &a[i]);  
            s ^= a[i];  
        }  
        for (int i = 0; i < n; i++)  
            if (a[i]>(a[i] ^ s))
                ans++;  
        printf("%d\n", ans);  
    }  

题解:上面总结的很好:

不是特别想写了,特别烦大哭大哭大哭

面对奇异局势,先取者必定输,如果是非奇异局势,则先取者赢!如果一个局势(a,b,c)有a(+)b(+)c==0,

则是奇异局势,如果不是,则不是奇异局势!三堆石子的结论同样也适用于n堆石子的情况!

自己理解一下吧!!下次再来

(④).I - 找规律博弈https://vjudge.net/contest/173215#problem/I

 题意:Alice和Bob没事就喜欢玩游戏,玩的还竟都是本身特别无聊但一出题就特别高端的那种拿石子游戏(……)

。给定几个堆的石子,A和B分别拿一个堆中任意数量的石子,然后可放可不放到其他有石子的堆中,谁最后没有

石子拿谁就输了。每次都是A先拿,输出1代表A赢,输出0代表B赢

发现现在做过的题,包括博弈论还有很多类型的题目,都是从最小的情况最简单的情况开始想,之后在往后面去

推,当然dp这种就更不用说了。假设就有一个堆,那肯定是A赢,因为A直接把所有的石子拿走,B就没有石子拿

了。假设有两个堆,如果是两个相等的堆,那就是B赢。因为A无论怎么拿,B都可以在另一个堆上做相同的动作,

导致一定是B拿到了最后的石子。如果是两个不同数量的堆,那又一定是A赢,因为A先拿,可以导致上面相等的

堆的情况,而这时A、B的拿的顺序已经换过来了,所以A赢。所以这时我们会发现拿走石子可以,放回石子到其

他有石子的堆里这个动作是没有用的,因为A、B都采取最好策略,每一堆石子数量比不放回的情况多是没有用的,

最后也都是拿走的命。所以就这么一直地推下去,会发现整个过程就是看石子堆是不是一对一对的过程,奇数堆

的一定是A赢,偶数堆的要是一对一对就是B赢,否则就是A赢。这样代码就好写了

所以这次做题的经验就是以后拿到博弈的题目,都要从最小最简单的情况搞起,再往后面推是一个好方法。

直接看代码吧!比较简单:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int Hash[110];
int main(){
    int n;
    int ans;
    int a;
    while(scanf("%d",&n)&&n)
    {
        ans=0;
        memset(Hash,0,sizeof(Hash));
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a);
            if(Hash[a])//记录并排除相等的情况
            {
                Hash[a]=0;//将出现相等数标记为“0”
                ans++;//记录相等数量的个数
            }
            else
                Hash[a]=1;//不相等的数标记为”1“,
        }
        if(n%2==0&&ans==n/2)//直接判断B输的情况
        //偶数堆且数量要一半一半的相等;
            printf("0\n");
        else
            printf("1\n");
    }
    return 0;
}



(四).知识点

异或:

异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法

为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位。

例:

部分计算机语言用1表示真,用0表示假,所以两个字节按位异或如下


00000000
xor
00000000
----------------------------------

00000000

============我是分界线1============

下面是两个二进制数值进行异或计算:


11111111
xor
00000000
----------------------------

11111111

============我是分界线2============

现实中用的都是十进制的数值,那么我们来看一看两个十进制数值是怎么进行异或计算:

5 ⊕ 2 = ?

1.进行异或计算前会把数值都转换为二进制的:

5和2转为二进制分别为:0101 、0010


0101
xor
0010
----------------------------

0111

2.再把结果 0111 转换为十进制的:7

3.所以 5 ⊕ 2 = 7

异或的作用:

1、交换两个整数的值而不必用第三个参数

a = 9;
b = 11;

a=a^b; 1001^1011=0010
b=b^a; 1011^0010=1001
a=a^b;  0010^1001=1011

a = 11;
b = 9;

2、奇偶判断

^a操作就是将a中的每一位按位逐一进行异或,例如a=4'b1010,则b=1^0^1^0=0,由此可以判断a中为1的位数是奇数

还是偶数,是一个便捷的操作。






原创粉丝点击