Skip to content
Vamshi Jandhyala

Books · Solving Puzzles through Mathematical Programming

Chapter 12

Monkey, Cat, and Dog

A monkey fills an n×nn \times n grid with the numbers 1,2,,n21, 2, \ldots, n^2, each used exactly once. A cat computes the product of the numbers in each row; a dog computes the product of the numbers in each column. Both animals write their results as a list of nn numbers. Can the monkey fill in the grid so that the cat and the dog obtain the same list (as a multiset)?

The answer depends on nn. For n=2n = 2 it is no, by a two-line argument. For n=3n = 3 it is yes, as witnessed by the grid 561247398\begin{array}{|c|c|c|} \hline 5 & 6 & 1 \\ \hline 2 & 4 & 7 \\ \hline 3 & 9 & 8 \\ \hline \end{array} whose row products are {30,56,216}\{30, 56, 216\} and whose column products are {30,216,56}\{30, 216, 56\}: the same multiset. For n{3,4,5,6,7,8}n \in \{3, 4, 5, 6, 7, 8\} the answer is yes, established constructively in each case by CP-SAT below. For n=9n = 9 the answer reverts to no — a surprising return of infeasibility — and the obstruction generalises to infinitely many larger nn.

Impossibility at n=2n = 2

Write the grid as (abcd)\bigl(\begin{smallmatrix} a & b \\ c & d \end{smallmatrix}\bigr) with {a,b,c,d}={1,2,3,4}\{a, b, c, d\} = \{1, 2, 3, 4\}. The row products are {ab,cd}\{ab, cd\} and the column products are {ac,bd}\{ac, bd\}. If the two multisets match, either ab=acab = ac and cd=bdcd = bd, which forces b=cb = c; or ab=bdab = bd and cd=accd = ac, which forces a=da = d. Either way, two of the four cells carry the same number, contradicting the assumption that the four cells hold the four distinct numbers 1,2,3,41, 2, 3, 4.

The programming model

A naive encoding introduces an integer variable per cell and a product-variable per row and per column, with AllDifferent\operatorname{AllDifferent} on the cells and multiset equality on the product lists. The products for large nn are immense — 10!3.6×10610! \approx 3.6 \times 10^6, 15!1.3×101215! \approx 1.3 \times 10^{12} — which rules the naive encoding out. A much tighter formulation replaces products with exponent vectors.

Primes and exponents

For each prime pn2p \leq n^2, define vp(k)v_p(k) to be the exponent of pp in the prime factorisation of kk. The product of a row is uniquely determined by its vector of prime exponents; two products are equal if and only if their exponent vectors are equal coordinate-by-coordinate. For each row ii and each prime pp the row’s total exponent of pp is Ri,p  =  j=1nvp(gi,j),R_{i, p} \;=\; \sum_{j = 1}^{n} v_p(g_{i, j}), and symmetrically Cj,pC_{j, p} for columns.

Cell-exponent variables

For each cell (i,j)(i, j) introduce an integer variable gi,j{1,,n2}g_{i, j} \in \{1, \ldots, n^2\} (with a single global AllDifferent\operatorname{AllDifferent} constraint over the n2n^2 cells), and for each prime pp a companion variable ei,j,pe_{i, j, p} representing vp(gi,j)v_p(g_{i, j}). Link the two via an element constraint against the lookup table [vp(1),vp(2),,vp(n2)][v_p(1), v_p(2), \ldots, v_p(n^2)] indexed by gi,j1g_{i, j} - 1.

Multiset equality

The row and column product multisets are equal if and only if there exists a permutation σSn\sigma \in S_n such that, for every prime pp, Ri,p  =  Cσ(i),p,i{1,,n}.R_{i, p} \;=\; C_{\sigma(i), p}, \quad i \in \{1, \ldots, n\}. This is encoded in CP-SAT by an integer permutation variable σi{0,,n1}\sigma_i \in \{0, \ldots, n-1\} with AllDifferent\operatorname{AllDifferent}, followed by an element constraint that equates Ri,pR_{i, p} to the σi\sigma_i-th entry of C,pC_{\cdot, p}, for each prime pp.

A solver in fifty lines

from ortools.sat.python import cp_model
from sympy import primerange, factorint

def solve(n):
    N = n * n
    primes = list(primerange(2, N + 1))
    v = {k: {p: 0 for p in primes} for k in range(1, N + 1)}
    for k in range(2, N + 1):
        for p, e in factorint(k).items():
            v[k][p] = e
    M = {p: max(v[k][p] for k in range(1, N + 1))
         for p in primes}

    m = cp_model.CpModel()
    g = [[m.NewIntVar(1, N, "")
          for _ in range(n)] for _ in range(n)]
    m.AddAllDifferent([g[i][j]
                       for i in range(n)
                       for j in range(n)])

    e = {}
    for i in range(n):
        for j in range(n):
            idx = m.NewIntVar(0, N - 1, "")
            m.Add(idx == g[i][j] - 1)
            for p in primes:
                tbl = [v[k][p] for k in range(1, N + 1)]
                x = m.NewIntVar(0, M[p], "")
                m.AddElement(idx, tbl, x)
                e[(i, j, p)] = x

    R, C = {}, {}
    for i in range(n):
        for p in primes:
            r = m.NewIntVar(0, n * M[p], "")
            m.Add(r == sum(e[(i, j, p)]
                           for j in range(n)))
            R[(i, p)] = r
    for j in range(n):
        for p in primes:
            c = m.NewIntVar(0, n * M[p], "")
            m.Add(c == sum(e[(i, j, p)]
                           for i in range(n)))
            C[(j, p)] = c

    sigma = [m.NewIntVar(0, n - 1, "")
             for _ in range(n)]
    m.AddAllDifferent(sigma)
    for i in range(n):
        for p in primes:
            col_list = [C[(j, p)] for j in range(n)]
            tgt = m.NewIntVar(0, n * M[p], "")
            m.AddElement(sigma[i], col_list, tgt)
            m.Add(R[(i, p)] == tgt)

    s = cp_model.CpSolver()
    s.parameters.max_time_in_seconds = 60
    st = s.Solve(m)
    if st not in (cp_model.OPTIMAL, cp_model.FEASIBLE):
        return None
    return [[s.Value(g[i][j]) for j in range(n)]
            for i in range(n)]

On a standard laptop the solver decides feasibility as follows: n=2:unsat,10 ms;n=3:sat,10 ms;n=4:sat,30 ms;n = 2 : \text{unsat}, 10 \text{ ms}; \quad n = 3 : \text{sat}, 10 \text{ ms}; \quad n = 4 : \text{sat}, 30 \text{ ms}; n=5:sat,0.12 s;n=6:sat,0.3 s;n=7:sat,1.4 s;n = 5 : \text{sat}, 0.12 \text{ s}; \quad n = 6 : \text{sat}, 0.3 \text{ s}; \quad n = 7 : \text{sat}, 1.4 \text{ s}; n=8:sat,2.8 s;n=9:unsat,6.3 s.n = 8 : \text{sat}, 2.8 \text{ s}; \quad n = 9 : \text{unsat}, 6.3 \text{ s}.

The n=3n = 3 instance is Figure 12.1; representative solutions for n=4n = 4 and n=5n = 5 are Figures 12.2 and 12.3.

An n=3n = 3 solution. Row products in warm orange to the right; column products in green along the bottom. The two lists are multiset-equal.
An n=4n = 4 solution.
An n=5n = 5 solution.

Why n=9n = 9 fails: large primes

The unexpected infeasibility at n=9n = 9 comes from a clean pigeonhole argument on large primes. A prime pp with n2/2<pn2n^2 / 2 < p \leq n^2 divides exactly one value in {1,2,,n2}\{1, 2, \ldots, n^2\}, namely pp itself (since 2p>n22p > n^2). Whichever row contains the value pp is the only row whose product is divisible by pp, and similarly for columns. The bijection σ\sigma that witnesses multiset equality must therefore map the row containing pp to the column containing pp: this fixes one entry of σ\sigma per such prime.

Now count the primes in (n2/2,n2](n^2 / 2, n^2]: n=9:{41,43,47,53,59,61,67,71,73,79},ten primes.n = 9 : \{41, 43, 47, 53, 59, 61, 67, 71, 73, 79\}, \quad \text{ten primes}. There are ten “large primes” but only nine rows. By pigeonhole, two of these primes lie in the same row, at different columns. The bijection σ\sigma is forced to map that row to both columns at once, which is impossible. No solution can exist.

This reasoning extends: feasibility fails whenever π(n2)π ⁣(n2/2)  >  n,\pi(n^2) - \pi\!\bigl(n^2 / 2\bigr) \;>\; n, where π\pi is the prime-counting function. The first offender is n=9n = 9; the next are n=11n = 11 (thirteen primes), n=12n = 12 (fourteen primes), and so on. By the prime-number theorem the left side grows like n2/(2lnn)n^2 / (2 \ln n), so the inequality holds for all sufficiently large nn; feasibility is therefore rare in the long run, and the feasible nn form a finite set.

The picture for small nn is then {3,4,5,6,7,8}:feasible;{2,9,11,12}:infeasible (by the counts above).\{3, 4, 5, 6, 7, 8\} : \text{feasible}; \qquad \{2, 9, 11, 12\} : \text{infeasible (by the counts above)}. The large-prime inequality is satisfied at n=11n = 11 and n=12n = 12 and, by the prime-number-theorem growth, at every sufficiently large nn, so all but finitely many nn are infeasible; but it does not follow that every n11n \geq 11 fails, since the inequality is not known to hold without exception, and establishing the precise infeasible set would require checking the remaining cases individually. The status of n=10n = 10 is in any case undetermined by the large-prime argument alone: there are exactly ten large primes in (50,100](50, 100], so the pigeonhole is tight but not strict, and a dedicated longer search is required to settle it.

Sources. The monkey–cat–dog problem appears as a training problem in Problem Solving Strategies (Arthur Engel, 19981998) and in various olympiad collections; the large-prime argument that rules out n=9n = 9 and all sufficiently large nn is due to multiple authors independently, perhaps first written up by R. Guy in Unsolved Problems in Number Theory. The feasibility of small nn is constructive but the constructions for n5n \geq 5 are not particularly illuminating; CP-SAT’s rapid decisions are the only tidy proof that each is feasible. The general classification of feasible nn — which finite set exactly? — combines the large-prime argument (ruling out infinitely many nn) with more delicate parity arguments at the remaining nn; an elementary complete classification is not known to the author.