Flea Circus
A 30 x 30 grid starts with exactly one flea on each square. Each round, every flea independently jumps to a uniformly random adjacent square (up, down, left, right). Corner fleas have 2 choices, ed...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
A \(30 \times 30\) grid of squares contains \(900\) fleas, initially one flea per square.
When a bell is rung, each flea jumps to an adjacent square at random (usually \(4\) possibilities, except for fleas on the edge of the grid or at the corners).
What is the expected number of unoccupied squares after \(50\) rings of the bell? Give your answer rounded to six decimal places.
Problem 213: Flea Circus
Mathematical Development
Definition 1. Let be the grid graph with and edges between horizontally or vertically adjacent squares. Let . Index the squares .
Definition 2. The transition matrix of the random walk on is defined by
where is the degree of vertex and means is adjacent to .
Lemma 1 (Row stochasticity). The matrix is row-stochastic: for all .
Proof. For any vertex , .
Theorem 1 (Chapman—Kolmogorov). The entry equals the probability that a random walk starting at vertex is at vertex after exactly steps.
Proof. By induction on . The base case holds by definition of . For the inductive step, , which by the inductive hypothesis equals , by the law of total probability.
Theorem 2 (Expected empty squares). Let be the indicator random variable for the event that square is empty after 50 rounds. Then
Proof. Step 1: By linearity of expectation (which holds without any independence assumption),
Step 2: Square is empty if and only if none of the fleas occupies it. Since fleas move independently (each flea’s random walk is independent of all others), the events are mutually independent across distinct . Therefore:
where is the probability that flea (starting at square ) is at square after 50 steps, by Theorem 1.
Remark. To avoid numerical underflow in the product , one may compute and exponentiate. The function is well-conditioned for small , and the library function log1p(-x) provides enhanced precision.
Editorial
We build transition matrix T. We then compute T^R via matrix exponentiation. Finally, accumulate expected empty squares. We enumerate the admissible parameter range, discard candidates that violate the derived bounds or arithmetic constraints, and update the final set or total whenever a candidate passes the acceptance test.
Pseudocode
Build transition matrix T
Compute T^R via matrix exponentiation
Accumulate expected empty squares
Complexity Analysis
Time. Matrix exponentiation via repeated squaring requires matrix multiplications, each costing . Thus the total is . For and , this is approximately . Alternatively, propagating each of the flea distributions individually over rounds costs , which is more practical. The product computation adds . Overall: .
Space. for storing the full probability matrix, or if accumulating products column-by-column.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
int main() {
const int R = 30, C = 30;
const int N = R * C;
const int ROUNDS = 50;
auto idx = [&](int r, int c) { return r * C + c; };
int dr[] = {-1, 1, 0, 0};
int dc[] = {0, 0, -1, 1};
vector<int> deg(N);
for (int r = 0; r < R; r++)
for (int c = 0; c < C; c++) {
int d = 0;
for (int k = 0; k < 4; k++)
if (r + dr[k] >= 0 && r + dr[k] < R && c + dc[k] >= 0 && c + dc[k] < C)
d++;
deg[idx(r, c)] = d;
}
vector<double> prod_not(N, 1.0);
for (int sr = 0; sr < R; sr++) {
for (int sc = 0; sc < C; sc++) {
vector<double> cur(N, 0.0), nxt(N, 0.0);
cur[idx(sr, sc)] = 1.0;
for (int t = 0; t < ROUNDS; t++) {
fill(nxt.begin(), nxt.end(), 0.0);
for (int r = 0; r < R; r++)
for (int c = 0; c < C; c++) {
int i = idx(r, c);
if (cur[i] == 0.0) continue;
double p = cur[i] / deg[i];
for (int k = 0; k < 4; k++) {
int nr = r + dr[k], nc = c + dc[k];
if (nr >= 0 && nr < R && nc >= 0 && nc < C)
nxt[idx(nr, nc)] += p;
}
}
swap(cur, nxt);
}
for (int i = 0; i < N; i++)
prod_not[i] *= (1.0 - cur[i]);
}
}
double answer = 0.0;
for (int i = 0; i < N; i++)
answer += prod_not[i];
cout << fixed << setprecision(6) << answer << endl;
return 0;
}
import numpy as np
def solve():
R, C = 30, 30
N = R * C
ROUNDS = 50
def idx(r, c):
return r * C + c
dr = [-1, 1, 0, 0]
dc = [0, 0, -1, 1]
deg = np.zeros(N, dtype=float)
for r in range(R):
for c in range(C):
deg[idx(r, c)] = sum(
1 for k in range(4) if 0 <= r + dr[k] < R and 0 <= c + dc[k] < C
)
T = np.zeros((N, N), dtype=float)
for r in range(R):
for c in range(C):
i = idx(r, c)
for k in range(4):
nr, nc = r + dr[k], c + dc[k]
if 0 <= nr < R and 0 <= nc < C:
T[i, idx(nr, nc)] = 1.0 / deg[i]
Tn = np.linalg.matrix_power(T, ROUNDS)
log_prod_not = np.sum(np.log1p(-Tn), axis=0)
answer = np.sum(np.exp(log_prod_not))
print(f"{answer:.6f}")
if __name__ == "__main__":
solve()