Integer and Rational Solutions to a Binary Quadratic Equation (1): Legendre equation
In this post, we’re interested in finding any or all rational solutions of the following equation:
or alternatively, all integer solutions to the Legendre equation:
where .
Legendre equation and normal form
(Normalization) We say the Legendre equation is in normal form if is square-free. The following transformation by Gauss reduces a Legendre equation to normal form: Write for square-free integers and integers , then
and are square-free and pair-wise coprime.
(Solubility) Legendre gave an elegant theorem to determine the solubility of an equation in normal form:
Legendre Equation \eqref{eq
} in normal form has a non-trivial integer solution iff. have different sign, and are quadratic residues modulo respectively.The if direction follows by considering modulo individually.
Find a single non-trivial integer solution
Brute-force
Holtz proved that there exists a small non-trivial solution:
If Equation \eqref{eq
} in normal form has a non-trivial solution, then there exists a non-trivial solution such that:Additionally, is a square if and only if , since is square-free. The same holds for and .
See [Mordell, Chapter 7, Theorem 5], [CR03] and [CM97] for more details.
Legendre’s descent
(This subsection is mostly taken from [Aitken, Section 5].)
Apparently, is equivalent to . Legendre’s descent is based on the following observation:
for . It allows us to reduce the problem to another problem . When , the other problem is smaller as we can always choose so .
The algorithm runs as follows:
# given a, b, find (x, y, z) such that a x^2 + b y^2 = z^2
def solve(a, b):
assert is_squarefree(a) and is_squarefree(b)
assert a >= 0 or b >= 0
if abs(a) > abs(b):
y, x, z = solve(b, a)
return (x, y, z)
if a == 0: return (1, 0, 0)
if b == 1: return (0, 1, 1)
if a == 1: return (1, 0, 1)
# find u such that u^2 == a (mod b) and -b / 2 <= u < b / 2
# WHAT IF u DOES NOT EXIST?
u = sqrtmod(a, b)
u = min(u, b - u)
b2 = (u * u - a) // b
# b2 = sqf_b2 * sq_b2^2 where sqf_b2 is square-free
sqf_b2, sq_b2 = squarefree(b2)
x2, y2, z2 = solve(a, sqf_b2)
return (
u * x2 + z2,
sqf_b2 * sq_b2 * y2,
u * z2 + a * x2,
)
The key point of the algorithm is that must exist during all recursions. It’s proved that if is in normal form, always exists when calling .
See [CR03, Section 2.3], [Aitken] and [Hillgarter] for more details. [CR03] also optimizes the method but it’s beyond the scope of the post.
Lattice-based algorithm
(This section is taken from [CR03, Section 2.6].)
Another idea arises when going deeper in Legendre’s theorem.
We first discuss the case where any of is 1. W.L.O.G. we assume .
- If , then is a non-trivial solution.
- If , we can use Cornacchia’s algorithm to solve , and is a non-trivial solution.
Now we consider the general case. We define a set of points :
where is an arbitrary solution to , similarly for and . Let be the non-zero point in minimizing . It can be proved that there is a small solution in , which can be used to bound :
so we conclude that and the minimizer itself satisfies Holzer’s condition.
The final step is to determine the minimizer. [CR03, Section 2.6, p.17] shows that is a lattice, that is, for a (row-based) basis . It also gives an explicit expression for a basis . Moreover, the shortest vector in a 3D lattice can be found efficiently using LLL, as long as we have an arbitrary basis for :
Let be a 3D lattice where is LLL-reduced. Then the shortest vector in is in the form of where .
Here is the pseudocode:
def solve(a, b, c):
assert (abs(a) == 1) + (abs(b) == 1) + (abs(c) == 1) < 2
f = lambda v: sum(v * v * [a, b, c])
def get_B():
λ_a = sqrtmod(-b * c, a)
λ_b = sqrtmod(-a * b, c)
λ_c = sqrtmod(-a * c, b)
u, a_2 = invmod(b, c), invmod(a, b * c)
v, b_2 = (1 - u * b) // c, (1 - a * a_2) // (b * c)
α = b_2 * c * λ_a % a
β = u * a_2 * b * λ_b % (b * c)
γ = v * a_2 * c * λ_c % (b * c)
# the basis of {(x, y, z): a x^2 + b y^2 + c z^2 = 0 mod abc}
B1 = np.array([
[b * c, 0, 0],
[a * β, a, 0],
[α * β + γ, α, 1],
])
# the basis of {(x, y, z): a x^2 + b y^2 + c z^2 = 0 mod 2abc}
eps = lambda v: f(v) // (a * b * c) % 2
r = next(r for r in B1 if eps(r) != 0)
return np.array([v if eps(v) == 0 else v + r for v in B])
B = LLL(get_B(), dot=lambda u, v: sum(u * v * np.abs([a, b, c])))
for w in product([-1, 0, 1], [-1, 0, 1], [-1, 0, 1]):
if w != (0, 0, 0) and f(v := w @ B) == 0:
return v
# unreachable!
Check out [CR03, Section 2.6] and [CM97] for more details. [CM97] uses a different lattice and [CR03] improves upon it.
A single non-trivial rational solution to with
Assume and is symmetric. We here only list a few references below:
- [Simon05] removes the need of diagonalization, which often bloats the size of coefficients. It gives an algorithm where . It works by reducing repeatedly until , and then solving the case where .
- [Simon06] extends [Simon05] and gives an algorithm for . However, I have not read the paper.
All non-trivial solutions from a single non-trivial solution
Given a single non-trivial solution to a quadratic equation , we parametrize by , where and is primitive (i.e., ).
Since , we solve in using the second-order Taylor expansion of at :
Since and produce the same solution, we require that is lexicographically larger than . Note this parametrization also generates the initial solution when , i.e., is tangent to the curve.
Connection to integer solutions of a homogenous ternary quadratic equation
A homogeneous polynomial of degree satisfies , so every rational solution can be normalized to a primitive integer solution. Furthermore, any primitive integer solution with is equivalent to a rational solution of a heterogeneous equation for .
When the goal is to enumerate integral solutions, a good strategy is to first find a non-trivial rational solution to and then apply the parametrization trick to a heterogeneous equation to avoid duplicates.
Consider the following example. We consider the homogenous equation in with a non-trivial primitive solution where , which is equivalent rational solutions to the heterogenous equation with a non-trivial solution . Write and apply the results above:
After simplification, we have
which are exactly the Equations (8-10) in Wolfram MathWorld. The equations might not generate primitive solutions directly, but they do produce all primitive tuples after normalization.
To use the equations to generate bounded primitive solutions, the common factor needs to be determined. It can be shown that divides by writing using :
and observing that if doesn’t divide , can’t be 1.
In our case, where . The bound can be slightly improved to by finding the LCM of denominators in :
If , the denominator can be further reduced to instead of .
References
- Rathnayake13: Rathnayake T., 2013. Solving Ternary Quadratic forms by descent method [online]
- [Mordell]: Mordell, L.J., 1969. Diophantine Equations: Diophantine Equations (Vol. 30). Academic press.
- CR03: Cremona, J. and Rusin, D., 2003. Efficient solution of rational conics. Mathematics of Computation, 72(243), pp.1417-1441.
- Simon05: Simon, D., 2005. Solving quadratic equations using reduced unimodular quadratic forms. Mathematics of Computation, 74(251), pp.1531-1543.
- Simon06: Simon, D., Quadratic equations in dimensions 4, 5 and more. Preprint, 2005 [online]
- CM97: Cochrane, T. and Mitchell, P., 1998. Small solutions of the Legendre equation. Journal of Number Theory, 70(1), pp.62-66.
- Aitken: Wayne Aitken, Legrendre’s Theorem, Legrange’s Descent
- Hillgarter: Hillgarter, E., 1996. Rational points on conics. na.
- [Holzer](Minimal Solutions of Diophantine Equations): Holzer, L., 1950. Minimal solutions of diophantine equations. Canadian Journal of Mathematics, 2, pp.238-244.
- It’s weird that I can’t find the first name of Holzer…