Problem (Euler, 1782). Six regiments each send six officers of six different ranks. Can the 36 officers stand in a square so that every row and every column contains each regiment once and each rank once?
Answer. No. The ranks form a Latin square (each symbol once per row and column), the regiments form another, and the requirement that every (rank, regiment) pair appears exactly once says the two squares are orthogonal. Orthogonal Latin squares (Graeco-Latin squares) exist for every order except 2 and 6.
| Order | Mutually orthogonal Latin squares (MOLS) | Source |
|---|---|---|
| prime power | , the maximum possible | finite field: |
| (prime powers) | at least | MacNeish: Kronecker products |
| at least 2 | MacNeish (every prime-power factor is ) | |
| 1 (no orthogonal pair) | trivial | |
| 1 (no orthogonal pair) | Tarry (1900); exhaustive search below | |
| , | at least 2 | Bose, Shrikhande and Parker (1959), disproving Euler’s conjecture |
| any | at most | normalization argument |
| 1 | 2 | 3 | 4 | 5 | 6 | |
|---|---|---|---|---|---|---|
| reduced Latin squares (first row and column in order) | 1 | 1 | 1 | 4 | 56 | 9408 |
| all Latin squares reduced | 1 | 2 | 12 | 576 | 161 280 | 812 851 200 |
using Square = vector<vector<int>>;
// Finite field GF(p^k): elements 0..q-1 are polynomials over Z_p written in base p.
struct GF {
int p, k, q; vector<vector<int>> add, mul;
GF(int p, int k) : p(p), k(k), q(1) {
for (int i = 0; i < k; i++) q *= p;
auto digits = [&](int x) { vector<int> d(k); for (int i = 0; i < k; i++) d[i] = x % p, x /= p; return d; };
auto pmul = [&](const vector<int>& a, const vector<int>& b) { vector<int> c(sz(a) + sz(b) - 1); for (int i = 0; i < sz(a); i++) for (int j = 0; j < sz(b); j++) c[i + j] = (c[i + j] + a[i] * b[j]) % p; return c; };
auto reduce = [&](vector<int> c, const vector<int>& f) { // c mod monic f of degree k
for (int i = sz(c) - 1; i >= k; i--) if (int t = c[i]) for (int j = 0; j <= k; j++) c[i - k + j] = ((c[i - k + j] - t * f[j]) % p + p) % p;
c.resize(k); return c;
};
vector<int> f; // monic irreducible of degree k: no nonzero x has x * y = 0
for (int low = 0; low < q && f.empty(); low++) {
vector<int> g = digits(low); g.push_back(1); bool ok = true;
for (int x = 1; x < q && ok; x++) for (int y = 1; y < q && ok; y++) {
auto r = reduce(pmul(digits(x), digits(y)), g); ok = *max_element(all(r)) > 0;
}
if (ok) f = g;
}
add.assign(q, vector<int>(q)); mul = add;
for (int x = 0; x < q; x++) for (int y = 0; y < q; y++) {
auto a = digits(x), b = digits(y); int s = 0, m = 0;
auto r = reduce(pmul(a, b), f);
for (int i = k - 1; i >= 0; i--) s = s * p + (a[i] + b[i]) % p, m = m * p + r[i];
add[x][y] = s; mul[x][y] = m;
}
}
vector<Square> mols() const { // the q - 1 squares L_a(x, y) = a x + y
vector<Square> res;
for (int a = 1; a < q; a++) { Square L(q, vector<int>(q)); for (int x = 0; x < q; x++) for (int y = 0; y < q; y++) L[x][y] = add[mul[a][x]][y]; res.push_back(L); }
return res;
}
};
Square kronecker(const Square& A, const Square& B) { // order |A| * |B|; preserves orthogonality pairwise
int a = sz(A), b = sz(B); Square C(a * b, vector<int>(a * b));
for (int i = 0; i < a * b; i++) for (int j = 0; j < a * b; j++) C[i][j] = A[i / b][j / b] * b + B[i % b][j % b];
return C;
}
bool orthogonal(const Square& A, const Square& B) {
int n = sz(A); vector<char> seen(n * n, 0);
for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { int x = A[i][j] * n + B[i][j]; if (seen[x]) return false; seen[x] = 1; }
return true;
}
// Orthogonal mate of L: partition the cells into n disjoint transversals (one cell per row and column, all symbols).
// Returns the mate, or an empty square if none exists.
Square orthogonalMate(const Square& L) {
int n = sz(L); vector<vector<int>> tr; vector<int> perm(n); iota(all(perm), 0);
do { int used = 0; bool ok = true; for (int i = 0; i < n && ok; i++) { ok = !(used >> L[i][perm[i]] & 1); used |= 1 << L[i][perm[i]]; } if (ok) tr.push_back(perm); }
while (next_permutation(all(perm)));
vector<vector<int>> byFirst(n); for (int t = 0; t < sz(tr); t++) byFirst[tr[t][0]].push_back(t);
vector<int> colUsed(n, 0), chosen; // colUsed[i]: columns of row i already taken
auto go = [&](auto&& self, int c) -> bool { // the transversal through cell (0, c)
if (c == n) return true;
for (int t : byFirst[c]) {
bool ok = true; for (int i = 0; i < n && ok; i++) ok = !(colUsed[i] >> tr[t][i] & 1);
if (!ok) continue;
for (int i = 0; i < n; i++) colUsed[i] |= 1 << tr[t][i];
chosen.push_back(t);
if (self(self, c + 1)) return true;
chosen.pop_back(); for (int i = 0; i < n; i++) colUsed[i] ^= 1 << tr[t][i];
}
return false;
};
if (!go(go, 0)) return {};
Square M(n, vector<int>(n)); for (int s = 0; s < n; s++) for (int i = 0; i < n; i++) M[i][tr[chosen[s]][i]] = s;
return M;
}Running orthogonalMate on all 9408 reduced Latin squares of order 6 finds no mate (a few seconds), which settles Euler’s problem.
Proof: at most n - 1 MOLS, and q - 1 for prime powers
Upper bound. Relabelling the symbols of one square keeps it orthogonal to the others, so assume every square has first row . In each square the cell holds a symbol (the column already has in row 0). Two different squares cannot hold the same there: the pair would appear both at and at . So there are at most squares.
Field construction. is Latin: for fixed the map is a bijection, and for fixed so is because is invertible. For , the pair gives , a unique , and then a unique : every pair appears once. The code finds a monic irreducible of degree by checking that has no zero divisors, which is exactly the condition for it to be a field.
Products. If (order ) and (order ), then : the pair at cell is evaluated at and , and as both cell coordinates range over everything, each pair of pairs occurs once. Factoring into prime powers and taking products of the first field squares of each factor gives MacNeish’s bound; if , every , so there are at least two MOLS.
Proof: orthogonal mates are transversal decompositions, and none exists for order 6
If , the cells where meet each row and column once and (by orthogonality) carry each symbol of once: a transversal of . The symbols of partition the cells into disjoint transversals; conversely such a partition defines by labelling the transversals.
orthogonalMatesearches this exactly: each transversal contains exactly one cell of row 0, so it picks the transversals through in turn, keeping them disjoint.Order 6. If some pair existed, relabel ‘s symbols so that its first row is , then permute the rows of both squares together so that ‘s first column is . Both operations keep the squares Latin and orthogonal, and is now reduced with a mate. The enumeration produces all 9408 reduced squares (matching the known count), and none has a mate. Mates are not automatic in other orders either: only 1 of the 4 reduced squares of order 4 (the Klein group table) and 6 of the 56 of order 5 have one. The addition table of for even has no transversal at all: a transversal would give , while the symbols it covers sum to .
Latin rectangles extend. A Latin rectangle () can always be extended by a row: in the bipartite graph joining column to the symbols missing from , every column has degree and every symbol is missing from exactly columns, so the graph is -regular and has a perfect matching (Hall / König); the matching is a valid next row. Repeating gives a full Latin square. Checked: all field squares for are pairwise orthogonal, Kronecker products of orthogonal pairs are orthogonal (orders 12, 15, 20), and the reduced-square counts match the table for .
Related notes
- Grid and Matrix Constructions
- Sudoku (a solved Sudoku is a Latin square)
- Kirkman’s Schoolgirl Problem (another design)
- Bipartite Matching (rectangle extension)