游戏中的博弈浅谈

本篇文章尝试讨论游戏类问题或者说博弈问题并给出一定的抽象,再在此抽象的基础上尝试给出一个一般性的解法,通过这种思路可以应对很多的算法题、面试题,也可在此基础上对解题过程进行一定的变化来解决一些变种问题。

 

一、理性人假设

一个游戏问题归根到底是双方(或多方)游戏玩家进行博弈的过程,为了使描述更为严谨,在描述游戏玩家的时候,隐含假设是该游戏中的玩家均为理性人,即会采取最为合理的策略使其获胜,若当前不存在使其获胜的策略,则其会在可选策略中进行随机的选取。

后文均在此假设之下来描述分析问题。

 

一、最简单的石子游戏

N块石头排成一行,每块石头有各自的位置,两个玩家依次取石头,每个玩家可以取任意一块或相邻块石头,最后能将石头一次取光的玩家获胜。

给定初始石头数目N,问先手玩家获胜还是后手玩家获胜。

(来源:《编程之美》1.11)

答案很简单,先手玩家必胜,先手玩家只需要保证取中间石头并保证左右两边严格对称即可获胜获胜,不论采用“聪明”的办法或者是较为“笨”的办法都不难得到这个结论。

 

二、石子问题的变种

N块石头排成一行,每块石头有各自的位置,两个玩家依次取石头,每个玩家可以取任意一块或两块石头,最后能将石头一次取光的玩家获胜。

给定初始石头数目N,问先手玩家获胜还是后手玩家获胜。

在分析问题的时候,我们可以简单得出一下表

剩余石头          胜利选手

1                       先手

2                       先手

3                       后手

4                       先手

5                       后手

。。。。。。。。。。。。

可以看出,在这个问题中,具体哪个玩家获胜依赖于起始的石头。

 

三、问题抽象

在分析第二个石子问题时,我们发现有些状态是先手必胜的,有些状态是后手必胜的(也就是先手必败的)

因为,我们把所有类似博弈类或游戏类的状态进行分类,并加以定义如下:

1、必胜态:在此状态行动的玩家必胜

2、必败态:在此状态行动的玩家必败

3、僵持态:在此状态行动的玩家胜败不定。

所有游戏的状态均是这三种状态组成的状态转换图。

现在则不难根据定义得到这三个状态的转换规则:

1、若一个状态能转移到必败态,那么此状态就是必胜态。(即如果我的一个策略能让对手进入必败态,那么我肯定采取这个策略,因此我肯定获胜,因此这是个必胜态)

2、若一个状态只能转移到必胜态,那么此状态是必败态。(无论我怎么做对手都必胜,那么我就必败)

3、除了必胜态和必败态其余都是僵持态。换句话说若一个状态可以转移到必胜态和僵持态,那么此状态就是一个僵持态。

大概的状态转换图如下:

                                    游戏中的博弈浅谈_第1张图片                        

接下来根据理性人的假设,所有玩家会尽可能的采取最优的策略去获取胜利,因此采用博弈论中的严格剔除劣势策略的方法,可以把必胜态到僵持态、僵持态到必胜态的转换剔除掉(很简单的道理,僵持时我没有必要走到胜态让对手获胜,胜态时没有必要重新走入僵持态浪费获胜的机会)。

因此状态转换图只有3个转换信息:从必胜态到必败态、从必败态到必胜态、一直停留在僵持态。

这就会得到一些很有意思的结论。

 

四、由问题抽象得到的结论

根据以上模型,我们可以得到以下结论:

1、假定游戏在有限步内结束,则给定初始条件,游戏的结果确定。

证明:若初始态为僵持态,由理性人假设,游戏会一直持续在僵持态,这与游戏在有限步内结束矛盾。故游戏初始态为必胜态或必败态,即游戏的结果确定。

2、若游戏可以不在有限步内结束,则给定初始条件,游戏或无限持续下去,或胜负已定。

证明过程:同理可证。。。

任何现实世界中的游戏或者博弈,均无法逃脱此框架。

比如象棋从某种意义上来说,开局双方一步未走的时候结局是已经确定的。但之所以会出现精彩纷呈的比赛原因在于现实世界中我们人并不能完全的遵从理性人的假设,我们无法遍历象棋巨大的状态空间并分析其中的必胜态、必败态和僵持态,进而就无法得到最合理的策略,从某种意义上来说,正是这种人的局限性,才使得现实世界中的博弈有如此多的看点。

 

五、抽象问题的动态规划求解

对于状态转换类问题使用动态规划无疑是一个很好且实用的算法,状态转换图前文已经分析过了,根据此状态转换图不难根据具体的游戏规则判断出必胜必败态,进而设计算法求出游戏中遇到的所有状态的类型,进而求得给定初始值游戏的胜负关系。

这里举ToperCoder SRM 575 DIV2 Level2的题目以及算法代码,因为比较简单,故不在加以解释

Problem Statement

  John and Brus play a game with a number. The game starts with a positive integern. The two players take alternating turns, John starts. Each move looks as follows: Let C be the current value of the integer. The current player has to choose a positive divisor of the number C, other than 1 and C. Once he chooses the divisor, he has to subtract it from C. The result is the new number with which the other player now starts his move. If a player cannot make a valid move, he loses the game.

For example, if they start with n=15, one possible gameplay can look as follows:
  • John takes the number 15, chooses its divisor 3, and decreases the number to 15-3 = 12.
  • Brus takes the number 12, chooses its divisor 4, and decreases the number to 12-4 = 8.
  • John takes the number 8, chooses its divisor 2, and decreases the number to 8-2 = 6.
  • Brus takes the number 6, chooses its divisor 3, and decreases the number to 6-3 = 3.
  • John takes the number 3, and as there are no divisors other than 1 and 3, he has no valid move and thus he loses the game.


You are given the int n. Assume that both players use the optimal strategy while playing the game. Return "John" (quotes for clarity) if John wins the game and "Brus" otherwise

            

 

import java.util.*;
public class TheNumberGameDivTwo {
	public int[] dpa;
	public String find(int n)
	{
		String result="";
		dpa=new int[n+1];
		dpa[0]=-1;
		dpa[1]=-1;
		for(int i=2;i<=n;i++)
		{
			if(isPrime(i))
			{
			dpa[i]=0;//lose
			}
			else
			{
				dpa[i]=-1;
			}
		}
		int r=dp(n);
		if(r==0)
		{
			result="Brus";
		}
		else
		{
			result="John";
		}
		return result;
	}
	public int dp(int s)
	{
		if(dpa[s]==-1)
		{
			for(int i=2;i<s;i++)
			{
				if(s%i==0)
				{
					if(dp(s-i)==0)
					{
						dpa[s]=1;
						break;
					}
					
				}
			}
			if(dpa[s]==-1)
			{
				dpa[s]=0;
			}
		}
		return dpa[s];
	}
	public boolean isPrime(int n)
	{
		for(int i=2;i<=Math.sqrt(n);i++)
		{
			if(n%i==0)
				return false;
		}
		return true;
	}
	public static void main(String[] argvs)
	{
		new TheNumberGameDivTwo().find(6);
	}
}


 

 

你可能感兴趣的:(N,博弈论,ToperCoder,游戏论)