1、题目
有一类问题总称为“随机漫步”(Random Walk)问题,这类问题长久以来吸引着数学界的兴趣。所有这些问题即使是最简单的解决起来也是极其困难的。而且它们在很大程度上还远没有得到解决。一个这样的问题可以描述为:
在矩形的房间里,铺有n×m块瓷砖,现将一只(醉酒的)蟑螂放在地板中间一个指定方格里。蟑螂随机地从一块瓷砖“漫步”到另一块瓷砖(可能是在找一片阿司匹林)。假设它可能从其所在的瓷砖移动到其周围八块瓷砖中的任何一个(除非碰到墙壁),那么它把每一块瓷砖都至少接触一次将花费多长时间?
虽然这个问题可能很难用纯粹的概率技术来解决,但是使用计算机的话却十分容易。使用计算机解决此问题的技术称为“模拟”。这种技术广泛应用于工业中,用来预测运输流量,存货控制等等。该问题可以采用如下方式进行模拟:
用一个n×m的数组作为计数器来表示蟑螂到达每一块瓷砖的次数,每个数组单元的初始化均置为零。蟑螂在地板上的位置用坐标(currentR,currentC)表示。蟑螂的八种可能移动用在位置(NextR = currentR + imove[k],NextC = currentC + jmove[k])的瓷砖表示,其中0≤k≤7,并且
imove[0] = -1; jmove[0] = 1; //东北方
imove[1] = 0; jmove[1] = 1; //正东方
imove[2] = 1; jmove[2] = 1; //东南方
imove[3] = 1; jmove[3] = 0; //正南方
imove[4] = 1; jmove[4] = -1; //西南方
imove[5] = 0; jmove[5] = -1; //正西方
imove[6] = -1; jmove[6] = -1; //西北方
imove[7] = -1; jmove[7] = 1; //正北方
蟑螂向其相邻的八个方格的随机漫步通过产生一个随机数值k(0≤k≤7)来模拟。当然,蟑螂不能爬出房间外,所以应该去掉通往墙壁的坐标并形成一个新的随机组合。蟑螂每次进入一个方格,该方格的计数器就增加1,从而计数器的一个非零元素就表示蟑螂到达对应方格的次数。每当一个方格被至少进入一次时,试验就完成了。
2、条件
程序必须满足:
①能够处理所有的n和m值, n和m满足:2<n≤40,2≤m≤20;
②能够对“n = 15,m = 15,起始点为(10,10)”和“n = 39,m = 19,起始点为(1,1)”进行实验。
③具有迭代限制,即实验过程中蟑螂进入方块的最大次数为MAX =50000时,程序能够终止。
3、输出
对于每次试验,打印:
①蟑螂进行的合法移动的总次数。
②最终的计数器数组,显示出漫步的“密度”,即输出在实验中每一块瓷砖被接经过的次数。
4、源程序
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int const MAX_LENGTH = 100;
int Random(int m,int n) //指定范围内随机数
{
int pos,dis;
if(m == n) //表示范围内只有一个数字
return m;
else if(m > n) //表示取区间[m,n]内的数字
{
pos = n;
dis = m - n + 1;
return rand() % dis + pos;
}
else //表示取区间[n,m]内的数字
{
pos = m;
dis = n - m + 1;
return rand() % dis + pos;
}
}
int rmove[8],cmove[8]; //移动方位数组
void InitMove(int* imove,int* jmove) //初始化移动方位数组
{
imove[0] = -1;
jmove[0] = 1; //东北方
imove[1] = 0;
jmove[1] = 1; //正东方
imove[2] = 1;
jmove[2] = 1; //东南方
imove[3] = 1;
jmove[3] = 0; //正南方
imove[4] = 1;
jmove[4] = -1; //西南方
imove[5] = 0;
jmove[5] = -1; //正西方
imove[6] = -1;
jmove[6] = -1; //西北方
imove[7] = -1;
jmove[7] = 1; //正北方
}
bool judgewalk(int* mark[],int n,int m) //判断是否访问完所有瓷砖
{
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
if(!mark[i][j]) //如果有一个没访问完的瓷砖,则退出
return false;
}
}
return true;
}
void printwalk(int* mark[],int n,int m) //打印所有瓷砖访问次数
{
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
cout << mark[i][j] << " ";
}
cout << endl;
}
}
void randomwalk(int** mark,int n,int m) //随机漫步
{
srand((int)time(NULL)); //根据时间产生相应种子值
InitMove(rmove,cmove); //初始化移动方位数组
int currentR = Random(0,n - 1); //现在所在行
int currentC = Random(0,m - 1); //现在所在列
mark[currentR][currentC]++;
int NextR,NextC; //下一位置所在行,下一位置所在列
int num = 0; //移动次数
while(true)
{
int moving = Random(0,7); //随机产生移动方向
NextR = currentR + rmove[moving]; //下一位置所在行
NextC = currentC + cmove[moving]; //下一位置所在列
while(NextR < 0 || NextC < 0 || NextR >=n || NextC >= m) //当碰到墙壁时
{
moving = Random(0,7); //随机产生移动方向
NextR = currentR + rmove[moving]; //下一位置所在行
NextC = currentC + cmove[moving]; //下一位置所在列
}
mark[NextR][NextC]++; //访问+1
currentR = NextR;
currentC = NextC; //下一位置等于当前位置
num++;
if(judgewalk(mark,n,m)) //判断是否访问完所有瓷砖
{
cout << "Total move step:" << num << endl; //打印总步数
printwalk(mark,n,m); //打印所有瓷砖访问次数
return;
}
if(num > 50000000) //超出访问上限次数
{
cout << "Cockroach don't visit all Tile." << endl;
cout << "Total move step:" << num << endl; //打印总步数
printwalk(mark,n,m);
return;
}
}
}
int main()
{
int *mark[MAX_LENGTH]; //指针数组
for(int i = 0; i < MAX_LENGTH; i++) //动态分配
mark[i] = new int[MAX_LENGTH];
for(int i = 0; i < MAX_LENGTH; i++) //初始化
for(int j = 0; j < MAX_LENGTH;j++)
mark[i][j] = 0;
int n,m; //行列
cin >> n >> m;
randomwalk(mark,n,m);
for(int i = 0; i < MAX_LENGTH; i++) //释放空间
delete[] mark[i];
return 0;
}