All Euler problems
Project Euler

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...

Source sync Apr 19, 2026
Problem #0213
Level Level 09
Solved By 2,734
Languages C++, Python
Answer 330.721154
Length 385 words
linear_algebraprobabilitygraph

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 G=(V,E)G = (V, E) be the grid graph with V={(r,c):0r,c<30}V = \{(r, c) : 0 \leq r, c < 30\} and edges between horizontally or vertically adjacent squares. Let N=V=900N = |V| = 900. Index the squares 1,,N1, \ldots, N.

Definition 2. The transition matrix TRN×NT \in \mathbb{R}^{N \times N} of the random walk on GG is defined by

Tij={1/deg(i)if ji,0otherwise,T_{ij} = \begin{cases} 1/\deg(i) & \text{if } j \sim i, \\ 0 & \text{otherwise}, \end{cases}

where deg(i){2,3,4}\deg(i) \in \{2, 3, 4\} is the degree of vertex ii and jij \sim i means jj is adjacent to ii.

Lemma 1 (Row stochasticity). The matrix TT is row-stochastic: j=1NTij=1\sum_{j=1}^{N} T_{ij} = 1 for all ii.

Proof. For any vertex ii, jTij=ji1/deg(i)=deg(i)(1/deg(i))=1\sum_{j} T_{ij} = \sum_{j \sim i} 1/\deg(i) = \deg(i) \cdot (1/\deg(i)) = 1. \square

Theorem 1 (Chapman—Kolmogorov). The entry (Tk)ij(T^k)_{ij} equals the probability that a random walk starting at vertex ii is at vertex jj after exactly kk steps.

Proof. By induction on kk. The base case k=1k = 1 holds by definition of TT. For the inductive step, (Tk+1)ij=(Tk)iTj(T^{k+1})_{ij} = \sum_{\ell} (T^k)_{i\ell} \cdot T_{\ell j}, which by the inductive hypothesis equals P(at  after k steps from i)P(move to j from )=P(at j after k+1 steps from i)\sum_{\ell} P(\text{at } \ell \text{ after } k \text{ steps from } i) \cdot P(\text{move to } j \text{ from } \ell) = P(\text{at } j \text{ after } k+1 \text{ steps from } i), by the law of total probability. \square

Theorem 2 (Expected empty squares). Let 1i\mathbf{1}_i be the indicator random variable for the event that square ii is empty after 50 rounds. Then

E ⁣[i=1N1i]=i=1Nj=1N(1(T50)ji).E\!\left[\sum_{i=1}^{N} \mathbf{1}_i\right] = \sum_{i=1}^{N} \prod_{j=1}^{N} \bigl(1 - (T^{50})_{ji}\bigr).

Proof. Step 1: By linearity of expectation (which holds without any independence assumption),

E ⁣[i=1N1i]=i=1NP(square i is empty).E\!\left[\sum_{i=1}^{N} \mathbf{1}_i\right] = \sum_{i=1}^{N} P(\text{square } i \text{ is empty}).

Step 2: Square ii is empty if and only if none of the NN fleas occupies it. Since fleas move independently (each flea’s random walk is independent of all others), the events {flea ji after 50 rounds}\{\text{flea } j \neq i \text{ after 50 rounds}\} are mutually independent across distinct jj. Therefore:

P(square i empty)=j=1NP(flea ji)=j=1N(1(T50)ji),P(\text{square } i \text{ empty}) = \prod_{j=1}^{N} P(\text{flea } j \neq i) = \prod_{j=1}^{N} \bigl(1 - (T^{50})_{ji}\bigr),

where (T50)ji(T^{50})_{ji} is the probability that flea jj (starting at square jj) is at square ii after 50 steps, by Theorem 1. \square

Remark. To avoid numerical underflow in the product j(1(T50)ji)\prod_j (1 - (T^{50})_{ji}), one may compute jlog(1(T50)ji)\sum_j \log(1 - (T^{50})_{ji}) and exponentiate. The function log(1x)\log(1 - x) is well-conditioned for small xx, 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 O(logR)O(\log R) matrix multiplications, each costing O(N3)O(N^3). Thus the total is O(N3logR)O(N^3 \log R). For N=900N = 900 and R=50R = 50, this is approximately 900364.4×109900^3 \cdot 6 \approx 4.4 \times 10^9. Alternatively, propagating each of the NN flea distributions individually over RR rounds costs O(NRN)=O(N2R)=O(900250)4×107O(N \cdot R \cdot N) = O(N^2 R) = O(900^2 \cdot 50) \approx 4 \times 10^7, which is more practical. The product computation adds O(N2)O(N^2). Overall: O(N2R)O(N^2 R).

Space. O(N2)O(N^2) for storing the full probability matrix, or O(N)O(N) if accumulating products column-by-column.

Answer

330.721154\boxed{330.721154}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_213/solution.cpp
#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;
}