📜  组合博弈论|组合3(脏数字/木材和墨西哥混合货币)

📅  最后修改于: 2021-04-29 01:00:07             🧑  作者: Mango

我们在组合1中介绍了组合博弈论,并在组合2中讨论了Nim博弈。
Grundy Number是定义游戏状态的数字。我们可以用Grundy Number定义任何公正的游戏(例如:nim游戏)。

一旦我们使用Sprague-Grundy定理计算出与该游戏相关联的Grundy Numbers,Grundy Numbers或Nimbers决定了如何解决任何公正游戏(不仅是Nim游戏)。
但是在计算Grundy Numbers之前,我们需要了解另一个术语-Mex。

什么是Mex?
一组数字中的“最小独占者”(又称“ Mex”)是该集合中不存在的最小非负数。

墨西哥

如何计算脏数字?
我们使用此定义-对于第一个玩家立即丢失的游戏,Grundy Number /积木数等于0,并且等于任何其他游戏所有下一个位置的积木数Mex。
下面是三个示例游戏和程序,用于分别计算它们的脏蛋数和墨西哥比索。粗略数的计算基本上是通过一个称为calculateGrundy()函数的递归函数来完成的,该函数使用calculateMex()函数作为其子例程。

例子1
游戏从一堆n块石头开始,并且要移动的玩家可能会拿到任何正数的石头。计算该游戏的脏数字。最后一个移动的玩家获胜。哪个玩家赢得比赛?
由于如果第一个玩家有0块石头,他将立即输掉,所以格伦迪(0)= 0
如果玩家有1块石头,那么他可以拿下所有石头并获胜。因此(对于另一位玩家)游戏的下一个可能位置是(0)石头
因此,Grundy(1)= Mex(0)= 1 [根据Mex的定义]
同样,如果玩家有2块石头,那么他只能拿1块石头,或者他可以拿走所有石头并获胜。因此(对于其他玩家)游戏的下一个可能位置分别是(1、0)石头。
因此,Grundy(2)= Mex(0,1)= 2 [根据Mex的定义]
同样,如果玩家有’n’石头,那么他只能拿1块石头,或者他可以拿2块石头……..或者他可以拿走所有石头并获胜。因此,游戏的下一个可能位置(对于其他玩家)分别是(n-1,n-2,…. 1)个石头。
因此,Grundy(n)= Mex(0,1,2,….n-1)= n [根据Mex的定义]

下表总结了第一个从0到10的Grundy值-

格兰迪1

C++
/* A recursive C++ program to find Grundy Number for
   a game which is like a one-pile version of Nim.
  Game Description : The game starts with a pile of n stones,
  and the player to move may take any positive number of stones.
The last player to move wins. Which player wins the game? */
#include
using namespace std;
 
// A Function to calculate Mex of all the values in
// that set.
int calculateMex(unordered_set Set)
{
    int Mex = 0;
 
    while (Set.find(Mex) != Set.end())
        Mex++;
 
    return (Mex);
}
 
// A function to Compute Grundy Number of 'n'
// Only this function varies according to the game
int calculateGrundy(int n)
{
    if (n == 0)
        return (0);
 
    unordered_set Set; // A Hash Table
 
    for (int i=0; i<=n-1; i++)
            Set.insert(calculateGrundy(i));
 
    return (calculateMex(Set));
}
 
// Driver program to test above functions
int main()
{
    int n = 10;
    printf("%d", calculateGrundy(n));
    return (0);
}


Java
// A recursive Java program to find Grundy
// Number for a game which is like a
// one-pile version of Nim. Game
// Description : The game starts
// with a pile of n stones, and the
// player to move may take any
// positive number of stones. 
// The last player to move wins.
// Which player wins the game?
import java.util.*;
 
class GFG{
     
// A Function to calculate Mex of all
// the values in that set.
public static int calculateMex(Set Set)
{
    int Mex = 0;
   
    while (Set.contains(Mex))
        Mex++;
   
    return (Mex);
}
   
// A function to Compute Grundy Number
// of 'n'. Only this function varies
// according to the game
public static int calculateGrundy(int n)
{
    if (n == 0)
        return (0);
         
    // A Hash Table
    Set Set = new HashSet();  
   
    for(int i = 0; i <= n - 1; i++)
        Set.add(calculateGrundy(i));
   
    return (calculateMex(Set));
}
 
// Driver code
public static void main(String[] args)
{
    int n = 10;
     
    System.out.print(calculateGrundy(n));
}
}
 
// This code is contributed by divyeshrabadiya07


Python3
''' A recursive Python3 program to find Grundy Number for
a game which is like a one-pile version of Nim.
Game Description : The game starts with a pile of n stones,
and the player to move may take any positive number of stones.
The last player to move wins. Which player wins the game? '''
 
# A Function to calculate Mex of all the values in
# that set.
def calculateMex(Set):
    Mex = 0
 
    while (Mex in Set):
        Mex += 1
 
    return (Mex)
 
# A function to Compute Grundy Number of 'n'
# Only this function varies according to the game
def calculateGrundy( n):
    if (n == 0):
        return (0)
 
    Set = set() # A Hash Table
 
    for i in range(n):
        Set.add(calculateGrundy(i));
 
    return (calculateMex(Set))
 
# Driver program to test above functions
n = 10;
print(calculateGrundy(n))
 
# This code is contributed by ANKITKUMAR34


C#
// A recursive C# program to find Grundy
// Number for a game which is like a
// one-pile version of Nim. Game
// Description : The game starts
// with a pile of n stones, and
// the player to move may take
// any positive number of stones.
// The last player to move wins.
// Which player wins the game?
using System;
using System.Collections;
using System.Collections.Generic;
 
class GFG{
 
// A Function to calculate Mex of all
// the values in that set.
static int calculateMex(HashSet Set)
{
    int Mex = 0;
   
    while (Set.Contains(Mex))
        Mex++;
   
    return (Mex);
}
   
// A function to Compute Grundy Number
// of 'n'. Only this function varies
// according to the game
static int calculateGrundy(int n)
{
    if (n == 0)
        return (0);
         
    // A Hash Table
    HashSet Set = new HashSet();
   
    for(int i = 0; i <= n - 1; i++)
            Set.Add(calculateGrundy(i));
   
    return (calculateMex(Set));
}   
 
// Driver code
public static void Main(string []arg)
{
    int n = 10;
     
    Console.Write(calculateGrundy(n));
}
}
 
// This code is contributed by rutvik_56


C++
/* A recursive C++ program to find Grundy Number for
a game which is one-pile version of Nim.
Game Description : The game starts with a pile of
n stones, and the player to move may take any
positive number of stones up to 3 only.
The last player to move wins. */
#include
using namespace std;
 
// A Function to calculate Mex of all the values in
// that set.
 
// A function to Compute Grundy Number of 'n'
// Only this function varies according to the game
int calculateGrundy(int n)
{
    if (n == 0)
        return (0);
    if (n == 1)
        return (1);
    if (n == 2)
        return (2);
    if (n == 3)
        return (3);
    else
        return (n%(3+1));
}
 
// Driver program to test above functions
int main()
{
    int n = 10;
    printf("%d", calculateGrundy(n));
    return (0);
}


Java
/* A recursive Java program to find
Grundy Number for a game which is
one-pile version of Nim.
Game Description : The game starts with
a pile of n stones, and the player to
move may take any positive number of stones
up to 3 only.The last player to move wins. */
import java.util.*;
 
class GFG
{
 
     
    // A function to Compute Grundy
    // Number of 'n' Only this function
    // varies according to the game
    static int calculateGrundy(int n)
    {
        if (n == 0)
            return 0;
        if (n == 1)
            return 1;
        if (n == 2)
            return 2;
        if (n == 3)
            return 3;
        else
            return (n%(3+1));
    }
 
    // Driver code
    public static void main(String[] args)
    {
        int n = 10;
        System.out.printf("%d", calculateGrundy(n));
    }
}
// This code is contributed by rahulnamdevrn27


Python3
# A recursive Python3 program to find Grundy Number
# for a game which is one-pile version of Nim.
# Game Description : The game starts with a pile
# of n stones, and the player to move may take
# any positive number of stones up to 3 only.
# The last player to move wins.
 
 
  
# A function to Compute Grundy Number of 'n'
# Only this function varies according to the game
def calculateGrundy(n):
 
    if 0 <= n <= 3:
        return n
     
    else:
        return (n%(3+1));
       
    
  
# Driver program to test above functions
if __name__ == "__main__":
  
    n = 10
    print(calculateGrundy(n))
     
# This code is contributed by rahulnamdevrn27


C#
/* A recursive Java program to find Grundy Number
for a game which is one-pile version of Nim.
Game Description : The game starts with a pile of
n stones, and the player to move may take any
positive number of stones up to 3 only.The last
player to move wins. */
using System;
using System.Collections.Generic;
 
class GFG
{
 
     
    // A function to Compute Grundy Number of
    // 'n' Only this function varies according
    // to the game
    static int calculateGrundy(int n)
    {
        if (n == 0)
            return 0;
        if (n == 1)
            return 1;
        if (n == 2)
            return 2;
        if (n == 3)
            return 3;
        else
            return (n%(3+1));
         
    }
 
    // Driver code
    public static void Main(String[] args)
    {
        int n = 10;
        Console.Write(calculateGrundy(n));
    }
}
// This code is contributed by rahulnamdevrn27


C++
/* A recursive C++ program to find Grundy Number for
   a game.
 Game Description:  The game starts with a number- 'n'
 and the player to move divides the number- 'n' with 2, 3
 or 6 and then takes the floor. If the integer becomes 0,
 it is removed. The last player to move wins.  */
#include
using namespace std;
 
// A Function to calculate Mex of all the values in
// that set.
int calculateMex(unordered_set Set)
{
    int Mex = 0;
 
    while (Set.find(Mex) != Set.end())
        Mex++;
 
    return (Mex);
}
 
// A function to Compute Grundy Number of 'n'
// Only this function varies according to the game
int calculateGrundy (int n)
{
    if (n == 0)
        return (0);
 
    unordered_set Set; // A Hash Table
 
    Set.insert(calculateGrundy(n/2));
    Set.insert(calculateGrundy(n/3));
    Set.insert(calculateGrundy(n/6));
 
    return (calculateMex(Set));
}
 
// Driver program to test above functions
int main()
{
    int n = 10;
    printf("%d", calculateGrundy (n));
    return (0);
}


Java
/* A recursive Java program to find Grundy Number for
a game.
Game Description : The game starts with a number- 'n'
and the player to move divides the number- 'n' with 2, 3
or 6 and then takes the floor. If the integer becomes 0,
it is removed. The last player to move wins. */
import java.util.*;
 
class GFG
{
 
    // A Function to calculate Mex of all the values in
    // that set.
    static int calculateMex(HashSet Set)
    {
        int Mex = 0;
 
        while (Set.contains(Mex))
        {
            Mex++;
        }
 
        return (Mex);
    }
 
    // A function to Compute Grundy Number of 'n'
    // Only this function varies according to the game
    static int calculateGrundy(int n)
    {
        if (n == 0)
        {
            return (0);
        }
 
        HashSet Set = new HashSet(); // A Hash Table
 
        Set.add(calculateGrundy(n / 2));
        Set.add(calculateGrundy(n / 3));
        Set.add(calculateGrundy(n / 6));
 
        return (calculateMex(Set));
    }
 
    // Driver code
    public static void main(String[] args)
    {
        int n = 10;
        System.out.printf("%d", calculateGrundy(n));
    }
}
 
// This code is contributed by PrinciRaj1992


Python3
# A recursive Python3 program to
# find Grundy Number for a game.
# Game Description : The game starts with a number- 'n'
# and the player to move divides the number- 'n' with 2, 3
# or 6 and then take the floor. If the integer becomes 0,
# it is removed. The last player to move wins.
 
# A Function to calculate Mex
# of all the values in that set.
def calculateMex(Set):
  
    Mex = 0
    while Mex in Set:
        Mex += 1
 
    return Mex
  
# A function to Compute Grundy Number of 'n'
# Only this function varies according to the game
def calculateGrundy(n):
  
    if n == 0:
        return 0
 
    Set = set() # A Hash Table
 
    Set.add(calculateGrundy(n // 2))
    Set.add(calculateGrundy(n // 3))
    Set.add(calculateGrundy(n // 6))
 
    return (calculateMex(Set))
  
# Driver program to test above functions
if __name__ == "__main__":
  
    n = 10
    print(calculateGrundy(n))
     
# This code is contributed by Rituraj Jain


C#
/* A recursive C# program to find Grundy Number for
a game.
Game Description: The game starts with a number- 'n'
and the player to move divides the number- 'n' with 2, 3
or 6 and then takes the floor. If the integer becomes 0,
it is removed. The last player to move wins. */
using System;
using System.Collections.Generic;
 
class GFG
{
 
    // A Function to calculate Mex of 
    // all the values in that set.
    static int calculateMex(HashSet Set)
    {
        int Mex = 0;
 
        while (Set.Contains(Mex))
        {
            Mex++;
        }
 
        return (Mex);
    }
 
    // A function to Compute Grundy Number of 'n'
    // Only this function varies according to the game
    static int calculateGrundy(int n)
    {
        if (n == 0)
        {
            return (0);
        }
 
        // A Hash Table
        HashSet Set = new HashSet();
 
        Set.Add(calculateGrundy(n / 2));
        Set.Add(calculateGrundy(n / 3));
        Set.Add(calculateGrundy(n / 6));
 
        return (calculateMex(Set));
    }
 
    // Driver code
    public static void Main()
    {
        int n = 10;
        Console.WriteLine(calculateGrundy(n));
    }
}
 
// This code is contributed by PrinciRaj1992


输出 :

10

上面的解决方案可以使用动态编程进行优化,因为存在重叠的子问题。可以在此处找到基于动态编程的实现。

例子2
游戏从一堆n块石头开始,并且要移动的玩家可以拿正数的石头,最多3块。最后一个移动的玩家获胜。哪个玩家赢得比赛?该游戏是Nim的1堆版本。
由于如果第一个玩家有0块石头,他将立即输掉,所以格伦迪(0)= 0
如果玩家有1块石头,那么他可以拿下所有石头并获胜。因此(对于另一位玩家)游戏的下一个可能位置是(0)石头

因此,Grundy(1)= Mex(0)= 1 [根据Mex的定义]
同样,如果玩家有2块石头,那么他只能拿1块石头,也可以拿2块石头赢。因此(对于其他玩家)游戏的下一个可能位置分别是(1、0)石头。
因此,Grundy(2)= Mex(0,1)= 2 [根据Mex的定义]
同样,Grundy(3)= Mex(0,1,2)= 3 [根据Mex的定义]

但是4块石头呢?
如果玩家有4块石头,那么他可以拿1块石头,也可以拿2块石头或3块石头,但不能拿4块石头(请参阅游戏限制)。因此,游戏的下一个可能位置(对于其他玩家)分别是(3,2,1,)石头。
因此,Grundy(4)= Mex(1、2、3)= 0 [根据Mex的定义]
因此我们可以递归地将n> = 4的Grundy数定义为-
Grundy(n)= Mex [Grundy(n-1),Grundy(n-2),Grundy(n-3)]

下表总结了第一个从0到10的Grundy值-

grundy2

C++

/* A recursive C++ program to find Grundy Number for
a game which is one-pile version of Nim.
Game Description : The game starts with a pile of
n stones, and the player to move may take any
positive number of stones up to 3 only.
The last player to move wins. */
#include
using namespace std;
 
// A Function to calculate Mex of all the values in
// that set.
 
// A function to Compute Grundy Number of 'n'
// Only this function varies according to the game
int calculateGrundy(int n)
{
    if (n == 0)
        return (0);
    if (n == 1)
        return (1);
    if (n == 2)
        return (2);
    if (n == 3)
        return (3);
    else
        return (n%(3+1));
}
 
// Driver program to test above functions
int main()
{
    int n = 10;
    printf("%d", calculateGrundy(n));
    return (0);
}

Java

/* A recursive Java program to find
Grundy Number for a game which is
one-pile version of Nim.
Game Description : The game starts with
a pile of n stones, and the player to
move may take any positive number of stones
up to 3 only.The last player to move wins. */
import java.util.*;
 
class GFG
{
 
     
    // A function to Compute Grundy
    // Number of 'n' Only this function
    // varies according to the game
    static int calculateGrundy(int n)
    {
        if (n == 0)
            return 0;
        if (n == 1)
            return 1;
        if (n == 2)
            return 2;
        if (n == 3)
            return 3;
        else
            return (n%(3+1));
    }
 
    // Driver code
    public static void main(String[] args)
    {
        int n = 10;
        System.out.printf("%d", calculateGrundy(n));
    }
}
// This code is contributed by rahulnamdevrn27

Python3

# A recursive Python3 program to find Grundy Number
# for a game which is one-pile version of Nim.
# Game Description : The game starts with a pile
# of n stones, and the player to move may take
# any positive number of stones up to 3 only.
# The last player to move wins.
 
 
  
# A function to Compute Grundy Number of 'n'
# Only this function varies according to the game
def calculateGrundy(n):
 
    if 0 <= n <= 3:
        return n
     
    else:
        return (n%(3+1));
       
    
  
# Driver program to test above functions
if __name__ == "__main__":
  
    n = 10
    print(calculateGrundy(n))
     
# This code is contributed by rahulnamdevrn27

C#

/* A recursive Java program to find Grundy Number
for a game which is one-pile version of Nim.
Game Description : The game starts with a pile of
n stones, and the player to move may take any
positive number of stones up to 3 only.The last
player to move wins. */
using System;
using System.Collections.Generic;
 
class GFG
{
 
     
    // A function to Compute Grundy Number of
    // 'n' Only this function varies according
    // to the game
    static int calculateGrundy(int n)
    {
        if (n == 0)
            return 0;
        if (n == 1)
            return 1;
        if (n == 2)
            return 2;
        if (n == 3)
            return 3;
        else
            return (n%(3+1));
         
    }
 
    // Driver code
    public static void Main(String[] args)
    {
        int n = 10;
        Console.Write(calculateGrundy(n));
    }
}
// This code is contributed by rahulnamdevrn27

输出 :

2

当允许我们捡起k块石头时,可以找到上述代码的一般解决方案。

例子3
游戏以数字“ n”开始,移动的玩家将数字“ n”除以2、3或6,然后发言。如果整数变为0,则将其删除。最后一个移动的玩家获胜。哪个玩家赢得比赛?

下表总结了第一个从0到10的Grundy值:

脏3

考虑一下我们如何生成此表。

C++

/* A recursive C++ program to find Grundy Number for
   a game.
 Game Description:  The game starts with a number- 'n'
 and the player to move divides the number- 'n' with 2, 3
 or 6 and then takes the floor. If the integer becomes 0,
 it is removed. The last player to move wins.  */
#include
using namespace std;
 
// A Function to calculate Mex of all the values in
// that set.
int calculateMex(unordered_set Set)
{
    int Mex = 0;
 
    while (Set.find(Mex) != Set.end())
        Mex++;
 
    return (Mex);
}
 
// A function to Compute Grundy Number of 'n'
// Only this function varies according to the game
int calculateGrundy (int n)
{
    if (n == 0)
        return (0);
 
    unordered_set Set; // A Hash Table
 
    Set.insert(calculateGrundy(n/2));
    Set.insert(calculateGrundy(n/3));
    Set.insert(calculateGrundy(n/6));
 
    return (calculateMex(Set));
}
 
// Driver program to test above functions
int main()
{
    int n = 10;
    printf("%d", calculateGrundy (n));
    return (0);
}

Java

/* A recursive Java program to find Grundy Number for
a game.
Game Description : The game starts with a number- 'n'
and the player to move divides the number- 'n' with 2, 3
or 6 and then takes the floor. If the integer becomes 0,
it is removed. The last player to move wins. */
import java.util.*;
 
class GFG
{
 
    // A Function to calculate Mex of all the values in
    // that set.
    static int calculateMex(HashSet Set)
    {
        int Mex = 0;
 
        while (Set.contains(Mex))
        {
            Mex++;
        }
 
        return (Mex);
    }
 
    // A function to Compute Grundy Number of 'n'
    // Only this function varies according to the game
    static int calculateGrundy(int n)
    {
        if (n == 0)
        {
            return (0);
        }
 
        HashSet Set = new HashSet(); // A Hash Table
 
        Set.add(calculateGrundy(n / 2));
        Set.add(calculateGrundy(n / 3));
        Set.add(calculateGrundy(n / 6));
 
        return (calculateMex(Set));
    }
 
    // Driver code
    public static void main(String[] args)
    {
        int n = 10;
        System.out.printf("%d", calculateGrundy(n));
    }
}
 
// This code is contributed by PrinciRaj1992

Python3

# A recursive Python3 program to
# find Grundy Number for a game.
# Game Description : The game starts with a number- 'n'
# and the player to move divides the number- 'n' with 2, 3
# or 6 and then take the floor. If the integer becomes 0,
# it is removed. The last player to move wins.
 
# A Function to calculate Mex
# of all the values in that set.
def calculateMex(Set):
  
    Mex = 0
    while Mex in Set:
        Mex += 1
 
    return Mex
  
# A function to Compute Grundy Number of 'n'
# Only this function varies according to the game
def calculateGrundy(n):
  
    if n == 0:
        return 0
 
    Set = set() # A Hash Table
 
    Set.add(calculateGrundy(n // 2))
    Set.add(calculateGrundy(n // 3))
    Set.add(calculateGrundy(n // 6))
 
    return (calculateMex(Set))
  
# Driver program to test above functions
if __name__ == "__main__":
  
    n = 10
    print(calculateGrundy(n))
     
# This code is contributed by Rituraj Jain

C#

/* A recursive C# program to find Grundy Number for
a game.
Game Description: The game starts with a number- 'n'
and the player to move divides the number- 'n' with 2, 3
or 6 and then takes the floor. If the integer becomes 0,
it is removed. The last player to move wins. */
using System;
using System.Collections.Generic;
 
class GFG
{
 
    // A Function to calculate Mex of 
    // all the values in that set.
    static int calculateMex(HashSet Set)
    {
        int Mex = 0;
 
        while (Set.Contains(Mex))
        {
            Mex++;
        }
 
        return (Mex);
    }
 
    // A function to Compute Grundy Number of 'n'
    // Only this function varies according to the game
    static int calculateGrundy(int n)
    {
        if (n == 0)
        {
            return (0);
        }
 
        // A Hash Table
        HashSet Set = new HashSet();
 
        Set.Add(calculateGrundy(n / 2));
        Set.Add(calculateGrundy(n / 3));
        Set.Add(calculateGrundy(n / 6));
 
        return (calculateMex(Set));
    }
 
    // Driver code
    public static void Main()
    {
        int n = 10;
        Console.WriteLine(calculateGrundy(n));
    }
}
 
// This code is contributed by PrinciRaj1992

输出 :

0

上面的解决方案可以使用动态编程进行优化,因为存在重叠的子问题。可以在此处找到基于动态编程的实现。