这个游戏就是小时候玩的大富翁,掷骰子来决定前进步数,到了某个方格可能会触发一些事件,转移到某个方格。
问题如果无限玩这个游戏,那么走到哪三个格子的概率最大呢?
一个比较正规的做法是建立一个四十乘四十的矩阵,是概率转移矩阵,记录每个格子转移到其他格子的概率,这个矩阵自乘成千上万次收敛之后,用初始状态,一个四十维的向量,和矩阵相乘,得到最终的四十维的向量,选择三个概率最高的格子。
但是我觉得一个一个算概率太麻烦,下面采用模拟游戏的方法,掷骰子一百万次,然后计算每个格子的次数,得到一个近似概率。
下面写贴出整个代码,然后通过注释的方式解释玩法、算法。
private static readonly int Rolls = 1_000_000;
// some special positions
private static readonly int Go = 0;
private static readonly int Jail = 10;
private static readonly int Go2Jail = 30;
private static readonly int C1 = 11;
private static readonly int E3 = 24;
private static readonly int H2 = 39;
private static readonly int R1 = 5;
private static readonly int[] Community = new int[] { 2, 17, 33 };
// 有三个 Chance 点,后面两个下一站坐标和 Chance 是对应的
private static readonly int[] Chance = new int[] { 7, 22, 36 };
private static readonly int[] NextRailway = new int[] { 15, 25, 5 };
private static readonly int[] NextUtility = new int[] { 12, 28, 12 };
public string GetAnswer()
{
// 记录每个到每个格子的次数
int[] board = new int[40];
Random random = new Random();
int doubles = 0;
int current = 0;
// Index,指代特殊牌的位置,也是特殊牌的值,详见下面的注释
int chanceIndex = 0;
int communityIndex = 0;
for (int i = 0; i < Rolls; i++)
{
// 这个题目和原始玩法不一样的地方,使用两个点数为 1-4 的骰子
int dice1 = random.Next(1, 5);
int dice2 = random.Next(1, 5);
// 如果两个骰子点数一样,记作 double
doubles = (dice1 == dice2) ? doubles + 1 : 0;
if (doubles > 2)
{
// 如果连续三次 double,直接进监狱
current = Jail;
doubles = 0;
}
else
{
// 没有连续三次 double,前进若干步
current = (current + dice1 + dice2) % 40;
// 现在考虑到了各种特殊的格子
//
// 处理 Chance
if (current == Chance[0] || current == Chance[1] || current == Chance[2])
{
// 使用一张特殊牌,切换位置
// 按理说,这两个应该在这个 if 的最后
// 但其实没有关系,先处理相当于整体牌的位置变化了一下而已
chanceIndex++;
chanceIndex %= 16;
// 这里记录 index 是为了跳转到对应的 Railway 或者 Utility
int index = 0;
if (current == Chance[1])
{
index = 1;
}
else if (current == Chance[2])
{
index = 2;
}
// 十六张特殊的牌,关于牌的堆叠,比较好的做法是初始化 1-16,
// 然后洗牌,在循环中摸牌,最后对各个点数进行处理
// 我这里取巧了一下,假定牌就是按照 1-16 码放的,理论上随机很多次之后
// 牌是如何码放的对最后结果没有影响
// 为了简化 chanceIndex 的操作,1-16 映射到了 0-15
switch (chanceIndex)
{
case 0:
current = Go;
break;
case 1:
current = Jail;
break;
case 2:
current = C1;
break;
case 3:
current = E3;
break;
case 4:
current = H2;
break;
case 5:
current = R1;
break;
case 6:
case 7:
current = NextRailway[index];
break;
case 8:
current = NextUtility[index];
break;
case 9:
current -= 3;
break;
}
}
// 处理 Community Chest
// 这里的思路和处理 Chance 一致
if (current == Community[0] || current == Community[1] || current == Community[2])
{
switch (communityIndex)
{
case 0:
current = Go;
break;
case 10:
current = Jail;
break;
}
communityIndex++;
communityIndex %= 16;
}
if (current == Go2Jail)
{
current = Jail;
}
}
board[current]++;
}
// 玩了一百万个回合了,排序
// 按照 Item 排序,Indx 记录原始的位置
int[] order = board.Select((item, indx) => new { Item = item, Index = indx })
.OrderByDescending(x => x.Item)
.Select(x => x.Index)
.ToArray();
// 选择前三个,拼接最后题目要求的字符串
string modalString = "";
for (int i = 0; i < 3; i++)
{
if (order[i] < 10)
{
modalString += "0";
}
modalString += order[i].ToString();
}
return modalString;
}
原文发表于我的小站
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。