Integer and Rational Solutions of a Binary Quadratic Equation (3): $ax^2+by^2=n$
In this post, we’re interested in finding all integer solutions of a binary quadratic equation:
for .
Normalization
(Pairwise coprime) Equation \eqref{eq
} is reduced if are pairwise coprime. We can reduce it as follows:- If and , then must divide (otherwise there is no solution), so we can reduce it to .
- If and but , then , so we can reduce it to .
- Similarly for and but .
Every time we make a reduction, decreases so it must terminate with a reduced equation.
(Primitive solutions) A solution is primitive if and only if . It’s trivial to use an algorithm which only finds all primitive solutions to find all solutions: If , then is a primitive solution to the equation .
From this point forward, we assume that Equation \eqref{eq
} is reduced unless stated otherwise, and we focus on the primitive solutions:For pairwise coprime integers , find all primitive solutions such that
Method 1: Lattice
(Partition of solution space) We take the idea from a method on solving Legendre equations. Let be a set of integer points in :
Obviously, contains all solutions to Equation \eqref{eq
}. We may partition into subsets: Let , thenNext, we need to determine the solutions in each individual .
(Lattice) To find the solutions in , we do it in a superset of , denoted as , and check if the solution is in . The superset is defined as:
It turns out that is a lattice with basis , . It’s also easy to check that every point in satisfies , thus, checking all minimizers of in suffices to find all solutions to Equation \eqref{eq
}.(Shortest vector) As in solving Legendre equation, we can use the same lemma for 3D lattice to efficiently find the shortest vector after reducing the basis by either Lenstra–Lenstra–Lovász algorithm or Lagrange-Gauss algorithm.
Let be a 2D lattice where the (row-based) basis is LLL-reduced. Then all shortest vectors in have form of where .
The following is the pseudocode for the complete algorithm. We need an efficient algorithm to solve , which was covered before. It’s also interesting to explore whether it is possible to avoid repeatly solving ‘s but that is left for future work. (Well, probably there won’t be any future work because the other method is much easier…)
def solve_primitive(a, b, n):
"""find all *primitive* solutions of (x, y) such that a x^2 + b y^2 = n"""
assert gcd(a, b) == 1 and gcd(b, n) == 1 and gcd(a, n) == 1
assert 1 <= a and 1 <= b and 1 <= n
dot = lambda u, v: sum(u * v * [a, b])
for λ in sqrtmod_all(-a * b, n):
B = np.array([
[n, 0],
[λ * invmod(a, n) % n, 1],
])
B = LLL(B, dot=dot)
for w in product([-1, 0, 1], [-1, 0, 1]):
if w != (0, 0) and gcd(x := w @ B) == 1 and dot(x, x) == n:
yield x
Method 2: The Hardy-Muskat-Williams algorithm
The most relevant paper is [HMW90], which introduces the Hardy-Muskat-Williams (HMW) algorithm. For pairwise copime integers such that , it finds all primitive solutions to with .
(Parition of solutions) Similarly, the HMW algorithm partitions the solutions using a linear congruence: For any such that , define
where pairs are unordered when , that is, and is considered the same solution when . It can be shown that .
_(Structure of )_ The HMW algorithm is based on the following elegant theorem:
Run the Euclidean algorithm on and and let be all the reminders:
i.e., . Let be the first remainder such that . If is not empty, then
If , then .
The implementation is surprisingly easy:
def solve_primitive(a, b, n):
"""find all positive primitive (unordered if a = b = 1) solutions such that ax^2 + by^2 = n"""
for t in sqrtmod_all(-b * invmod(a, n) % n):
if t >= n // 2: continue
p, x = n, t
while a * x * x >= n:
p, x = x, p % x
y = int(sqrt((n - a * x * x) // b))
if a * x * x + b * y * y == n:
yield x, y
A special case of the HMW algorithm is Cornacchia’s algorithm where . [Basilla04] proved the correctness of Cornacchia’s algorithm using same lattice as ours.
A failed attempt
The Brahmagupta–Fibonacci identity states that
so the attempt is to find all prime factors of and solve subproblems for each prime factors. However, not all solutions can be generated this way. A simple counterexample is , while and have no solutions.
It’s also related to the fact that is not a UFD for .
Reference
Due to an incomplete literature review, I initially overlooked a significant portion of previous work before arriving with the lattice algorithm. Later I came across Cornacchia’s algorithm algorithm, which can be further generalized to the HMW algorithm.
- Rathnayake13: Rathnayake T., 2013. Improving the solving process using Cornacchia [online]
- Basilla04: Basilla, J.M., 2004. On the solution of .
- Lattice Reduction: https://en.wikipedia.org/wiki/Lattice_reduction
- Deng16: Deng, X., 2016. An introduction to lenstra-lenstra-lovasz lattice basis reduction algorithm. Massachusetts Institute of Technology (MIT).
- Nguyen09: Nguyen, P.Q., 2009. Hermite’s constant and lattice algorithms. In The LLL Algorithm: Survey and Applications (pp. 19-69). Berlin, Heidelberg: Springer Berlin Heidelberg.
- HMW90: Hardy, K., Muskat, J.B. and Williams, K.S., 1990. A deterministic algorithm for solving in coprime integers 𝑢 and 𝑣. Mathematics of Computation, 55(191), pp.327-343.
Update @2025-03-13: Added the Hardy-Muskat-Williams algorithm.