Tunnell's Algorithm
Congruent number problem
The congruent number problem is ancient and asks which positive integers can be the area of a right triangle with all three sides rational. The problem can be approached by rephrasing in terms of rational points on a special family of Elliptic Curves. This allows us to use the machinery of the addition of points on ECs, and also connect to modular forms and finally to Tunnell’s Theorem which relates this to the number of integral solutions of a few fairly simple Diophantine equations.
Theorem
For a given square-free integer n, define
Tunnell’s theorem states that supposing n is a congruent number,
if n is odd then and if n is even then .
Conversely, if the Birch and Swinnerton-Dyer conjecture holds true for elliptic curves of the form
these equalities are sufficient to conclude that n is a congruent number.
Code
In this notebook I begin by counting solutions using a simple bound, and then generate square-free congruent numbers. Finally, I throw the squares back in to generate all congruent numbers up to a given bound as allcong(-)
.
import math
def number_of_sols(a, b, c, n): #Solves n = ax^2+by^2+cZ^2
count = 0
maxx = int((math.sqrt(n/a))) #int naturally takes floor
maxy = int((math.sqrt(n/b))) #everything positive so
maxz = int((math.sqrt(n/c))) #quick bounds such as y^2<n/a...
for x in range(-maxx,maxx+1):
for y in range(-maxy,maxy+1):
for z in range(-maxz,maxz+1):
if n == a*x*x+b*y*y+c*z*z:
count = count+1
return count
number_of_sols(1,1,1,1)
6
def An(n):
return number_of_sols(2,1,32,n)
def Bn(n):
return number_of_sols(2,1,8,n)
def Cn(n):
return number_of_sols(8,2,64,n)
def Dn(n):
return number_of_sols(8,2,16,n)
################################################
#The following ONLY WORKS WHEN n IS SQUARE-FREE#
################################################
def OddCong(n):
return 2*An(n)==Bn(n)
def EvenCong(n):
return 2*Cn(n)==Dn(n)
def prime_factors(n):
i = 2
factors = []
while i * i <= n:
if n % i:
i += 1
else:
n //= i
factors.append(i)
if n > 1:
factors.append(n)
return factors
prime_factors(100)
# set() removes duplicates
len(prime_factors(100)) == len(set(prime_factors(100)))
# Tests for unique prime factors (square free) by
# seeing if the full list of prime factors agrees
# with the list as a set (when repetition is removed).
False
def squarefreecong(p):
listsqfrcong =[]
upto = p
for n in range(1,upto+1):
if len(prime_factors(n)) == len(set(prime_factors(n))): #Only proceed if square-free
if n%2 != 0: #Odd
if OddCong(n) == 1:
listsqfrcong.append(n)
if n%2 == 0: #Even
if EvenCong(n) == 1:
listsqfrcong.append(n)
return listsqfrcong
print'The following are the square-free numbers up to 100 which are Congruent:'
squarefreecong(100)
The following are the square-free numbers up to 100 which are Congruent: [5, 6, 7, 13, 14, 15, 21, 22, 23, 29, 30, 31, 34, 37, 38, 39, 41, 46, 47, 53, 55, 61, 62, 65, 69, 70, 71, 77, 78, 79, 85, 86, 87, 93, 94, 95]
These are known as primitive congruent numbers.
If I now wish to generate all congruent numbers I look at the squares up to p and then multiply sets before truncating at p.
def squaresupperlim(q):
list =[]
for n in range(1, int(math.sqrt(q))+1):
list.append(n*n)
return list
squaresupperlim(85) #As an example
[1, 4, 9, 16, 25, 36, 49, 64, 81]
def allcong(n):
conglist=[]
for a in squarefreecong(n):
for b in squaresupperlim(n):
conglist.append(a*b)
finallist=[]
for c in conglist:
if c <= n:
finallist.append(c)
return sorted(finallist)
print(allcong(126))
[5, 6, 7, 13, 14, 15, 20, 21, 22, 23, 24, 28, 29, 30, 31, 34, 37, 38, 39, 41, 45, 46, 47, 52, 53, 54, 55, 56, 60, 61, 62, 63, 65, 69, 70, 71, 77, 78, 79, 80, 84, 85, 86, 87, 88, 92, 93, 94, 95, 96, 101, 102, 103, 109, 110, 111, 112, 116, 117, 118, 119, 120, 124, 125, 126]
Compared with downloaded list from OEIS:
CongruentData = [5,6,7,13,14,15,20,21,22,23,24,28,29,30,31,34,37,
38,39,41,45,46,47,52,53,54,55,56,60,61,62,63,65,
69,70,71,77,78,79,80,84,85,86,87,88,92,93,94,95,
96,101,102,103,109,110,111,112,116,117,118,119,
120,124,125,126]
CongruentData == allcong(126)
True
A deep problem (proving that n=1 is not congruent is equivant to the cubic case of FLT using an infinite descent arguemt…) is reduced to quick calculation :-)