A few days ago someone asked the following on math.stackexchange:
Is it possible to get arbitrarily near any acute angle with Pythagorean triangles?
A Pythagorean triangle is a right triangle with all integer sides. The sides of such a triangle are called Pythagorean triples. The most famous example is a 3-4-5 triangle. A little less known example is 5-12-13. Since not many Pythagorean triples come to mind, you might think there aren’t that many of them. Or maybe they’re unevenly distributed so that you can approximate some angles as well as you’d like but there’s a limit to how well could you approximate others.
It turns out you can indeed approximate any acute angle with that of a Pythagorean triangle. The answer on math.stackexchange explains why this is possible and here I give an algorithm. I wrote a little online calculator for finding Pythagorean triangle approximations based on the algorithm below.
A result going all the way back to Euclid says that (a, b, c) is a Pythagorean triple if and only if (a, b, c) = (m2 – n2, 2mn, m2 + n2) where m and n are positive integers with m > n. The tangent of the corresponding triangle is 2mn/(m2 – n2) = 2t/(1 – t2) where t = n/m. Given an acute angle θ, we need to find t such that tan(θ) = 2t/(1 – t2) and then find a rational approximation to t.
We can solve for t using the quadratic formula and find that t = (1 – cos(θ))/sin(θ). We can then find a rational approximation to t using Farey’s algorithm.
We’d like to not use larger numbers than necessary in our Pythagorean triangles. Suppose we don’t want the hypotenuse to be much more than H. If t ≈ n/m and m2 + n2 = H, then (1 + t2) m2 ≈ H. Therefore we’d like to look for rational approximations to t with denominator m no more than sqrt(H/(1 + t2)). This is approximate, however, since t is only approximately equal to n/m.
If we use the Python function
farey from a previous post, the code for finding a Pythagorean approximation to an acute angle θ is simply the following.
from math import sin, cos, sqrt def pythagorean_triple(acute_angle, max_hypoteneuse): t = (1 - cos(acute_angle))/sin(acute_angle) max_denominator = sqrt( max_hypoteneuse /(1 + t*t) ) n, m = farey(t, max_denominator) return m*m - n*n, 2*m*n, m*m + n*n
If the angle is small and the maximum hypotenuse is too small, the best rational approximation to t might be zero. In that case the algorithm returns a degenerate triangle, i.e. one of the sides has zero length. You could increase the maximum hypotenuse and try again until you get a valid triangle.
Update: The procedure above will return a Pythagorean triple, but not necessarily a primitive Pythagorean triple, i.e. the three numbers may have a common factor as Douglas pointed out in the comments. The triple is only primitive if m and n have opposite parity. You could guarantee a primitive triple by dividing by the greatest common divisor of the first two terms. (Anything that divides a and b must divide c.)
Here’s revised code that returns only primitive triples.
def gcd(a, b): if b == 0: return a else: return gcd(b, a%b) def pythagorean_triple(acute_angle, max_hypoteneuse): t = (1 - cos(acute_angle))/sin(acute_angle) max_denominator = sqrt( max_hypoteneuse /(1 + t*t) ) n, m = farey(t, max_denominator) a = m*m - n*n b = 2*m*n c = m*m + n*n g = gcd(a, b) a /= g b /= g c /= g return a, b, c