4x4棋盘棋子相遇问题

如题,今天专业课老师提的一个很有意思的问题。
问题内容:
有一个4x4棋盘,左上角和右下角各有一个棋子,一个棋子移动完后另一个棋子才能移动,棋子每次只会向上下左右的其中一个方向移动一格,若向边界方向移动则移动后的位置在另一边的最远处(比如棋子在左上角向上移动,它就会到左下角),问平均移动多少次两个棋子会撞上(在同一位置)
要求用C++代码编程并输出结果。
ps:不是作业,真的是老师随口提的一个问题。其他编程语言的大佬也可以来看看呀。

假设移动n次后,(n->无限大)。那么可以看做,一个棋子落在某个位置的概率是1/16.那么两个棋子在同一位置就是1/16X1/16=1/256。那么平均256次有一次会撞上。


#include <bits/stdc++.h>

using namespace std;

int xx[] = {1, -1, 0, 0};
int yy[] = {0, 0, 1, -1};
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n = 4;
    int m = 100000;
    unsigned seed = chrono::system_clock::now().time_since_epoch().count();
    mt19937 rand_num(seed);
    uniform_int_distribution<int> dist(0, 3);
    int sum = 0;
    for(int j=0;j<m;j++){
        vector<vector<int> > mp(n + 1, vector<int> (n + 1));
        vector<vector<int> > mp2(n + 1, vector<int> (n + 1));
        bool f = false;
        int ans = 0;
        function<void(int, int, int, int)> Dfs = [&](int x1, int y1, int x2, int y2){
            if(mp[x1][y1] || mp2[x2][y2]) return;
            if(f) return;
            // cout << x1 << ' ' << y1 << ' ' << y1 << ' ' << y2 << '\n';
            mp[x1][y1] = 1;
            mp2[x2][y2] = 1;
            int num = 0;
            int num2 = 0;        
            // cout << num << ' ' << num2 << '\n';
            if(num < 4){
                int dx = x1;
                int dy = y1;
                int dx2 = x2;
                int dy2 = y2;
                for(int i=0;i<4;i++){
                    int dx = x1 + xx[i];
                    int dy = y1 + yy[i];
                    if(dx == 0) dx += n;
                    if(dy == 0) dy += n;
                    if(dx == n + 1) dx -= n;
                    if(dy == n + 1) dy -= n;
                    if(mp[dx][dy] == 1) num += 1;
                    // cout << dx << ' ' << dy << ' ';

                    dx = x2 + xx[i];
                    dy = y2 + yy[i];
                    if(dx == 0) dx += n;
                    if(dy == 0) dy += n;
                    if(dx == n + 1) dx -= n;
                    if(dy == n + 1) dy -= n;
                    if(mp2[dx][dy] == 1) num2 += 1;
                    // cout << dx << ' ' << dy << '\n';
                }
                while(num < 4 && num2 < 4){
                    int k = dist(rand_num);
                    dx = x1 + xx[k];
                    dy = y1 + yy[k];
                    if(dx == 0) dx += n;
                    if(dy == 0) dy += n;
                    if(dx == n + 1) dx -= n;
                    if(dy == n + 1) dy -= n;

                    // cout << x1 << ' ' << y1 << ' ' << y1 << ' ' << y2 << '\n';
                    if(dx == x2 && dy == y2){
                        f = true;
                        return;
                    }
                    if(!mp[dx][dy]){
                        num += 1;
                        ans += 1;
                        num2 = 0;
                        for(int i=0;i<4;i++){
                            dx = x2 + xx[i];
                            dy = y2 + yy[i];
                            if(dx == 0) dx += n;
                            if(dy == 0) dy += n;
                            if(dx == n + 1) dx -= n;
                            if(dy == n + 1) dy -= n;
                            if(mp2[dx][dy] == 1) num2 += 1;
                            // cout << dx << ' ' << dy << '\n';
                        }
                        while(num2 < 4){
                            k = dist(rand_num);
                            dx2 = x2 + xx[k];
                            dy2 = y2 + yy[k];
                            if(dx2 == 0) dx2 += n;
                            if(dy2 == 0) dy2 += n;
                            if(dx2 == n + 1) dx2 -= n;
                            if(dy2 == n + 1) dy2 -= n;
                            // cout << x1 << ' ' << y1 << ' ' << x2 << ' ' << y2 << '\n';
                            if(dx2 == dx && dy2 == dy){
                                f = true;
                                return;
                            }
                            if(!mp2[dx2][dy2]){
                                num2 += 1;
                                ans += 1;
                                Dfs(dx, dy, dx2, dy2);
                            }
                        }
                    }
                }
            }
        };
        Dfs(1, 1, n, n);
        // cout << ans << '\n';
        sum += ans;
    }
    cout << sum * 1.0 / m;
    return 0;
}
你觉得我的随机化搜索有没有问题,看起来好像在7.5附近

感觉不该标记,改一下

#include <bits/stdc++.h>

using namespace std;

int xx[] = {1, -1, 0, 0};
int yy[] = {0, 0, 1, -1};
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n = 4;
    int m = 1000;
    unsigned seed = chrono::system_clock::now().time_since_epoch().count();
    mt19937 rand_num(seed);
    uniform_int_distribution<int> dist(0, 3);
    int t = 100;
    double res = 0.0;
    while(t--){
        int sum = 0;
        for(int j=0;j<m;j++){
            vector<vector<int> > mp(n + 1, vector<int> (n + 1));
            vector<vector<int> > mp2(n + 1, vector<int> (n + 1));
            bool f = false;
            int ans = 0;
            int ok = 0;
            function<void(int, int, int, int, int)> Dfs = [&](int x1, int y1, int x2, int y2, int op){
                if(f) return;
                if(ans >= 10000) return;
                if(op == 0){
                    int k = dist(rand_num);
                    int dx = x1 + xx[k];
                    int dy = y1 + yy[k];
                    if(dx == 0) dx = n;
                    if(dy == 0) dy = n;
                    if(dx == n + 1) dx = 1;
                    if(dy == n + 1) dx = 1;
                    ans += 1;
                    if(dx == x2 && dy == y2){
                        f = true;
                        return;
                    }
                    Dfs(dx, dy, x2, y2, op ^ 1);                    
                }else{
                    int k = dist(rand_num);
                    int dx = x2 + xx[k];
                    int dy = y2 + yy[k];
                    if(dx == 0) dx = n;
                    if(dy == 0) dy = n;
                    if(dx == n + 1) dx = 1;
                    if(dy == n + 1) dy = 1;
                    ans += 1;
                    if(dx == x1 && dy == y1){
                        f = true;
                        return;
                    }
                    Dfs(x1, y1, dx, dy, op ^ 1);                
                }

            };
            Dfs(1, 1, n, n, 0);
            // cout << ans << '\n';
            if(ans != 10000) sum += ans; 
        }
        // cout << sum * 1.0 / m << '\n';
        res += sum * 1.0 / m;
    }
    cout << fixed << setprecision(5) << res / 100 << '\n';
    return 0;
}

让这两个人乱走,结果好像在120左右,去除掉极端情况,可能需要作图看一下极限
也有可能是计算每个格子的期望然后加和,可能也能得到答案

我说一下我的思路,首先考虑,相遇的总次数,然后累加所有的相遇情况的步数,最后总步数除以总次数。总步数好算。剩下考虑总的相遇情况的次数。假如次数恒定,结果就是恒定的,假如次数不恒定,那结果我就不知道了

这个问题的解法,是以A在左上角,并且B在右下角为初始条件,经过n次移动后相遇,记录相遇时的移动次数,作为一个样本。然后从初始位置开始,再次模拟随机移动,产生下一个样本。通过大量的随机试验,统计平均次数。

一开始以为是持续移动,没有每次从初始位置开始,所以统计数为16。

按照这种测试方法,大量试验的统计数概率,大约是18.67。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>

uint32_t move2meet(void)
{
    uint32_t move_cnt = 0;
    int ax = 0, ay = 0;
    int bx = 3, by = 3;
    
    int move_x[4] = {0,0,3,1}; // 上下左右
    int move_y[4] = {3,1,0,0};
    int dir;

    // 每次循环是A走一步和B走一步,相当于2步
    do {
        move_cnt++;
        dir = rand() % 4;
        ax = (ax + move_x[dir]) % 4;
        ay = (ay + move_y[dir]) % 4;
        if (ax == bx && ay == by) {
            break;
        }

        move_cnt++;
        dir = rand() % 4;
        bx = (bx + move_x[dir]) % 4;
        by = (by + move_y[dir]) % 4;
        if (ax == bx && ay == by) {
            break;
        }
    } while (!(ax == bx && ay == by));

    return move_cnt;
}

void cal(int loop)
{
    int i;
    uint32_t move_sum = 0;
    for (i = 0; i < loop; ++i) {
        move_sum += move2meet();
    }
    printf("loop %d, move_sum %d, average %0.2f\n", loop, move_sum, (float)move_sum/loop);
}

int main()
{
    srand((unsigned)time(NULL)); 

    cal(1000);
    cal(10000);
    cal(100000);

    return 0;
}

loop 1000, move_sum 17934, average 17.93
loop 10000, move_sum 185250, average 18.52
loop 100000, move_sum 1866686, average 18.67