Wednesday, March 4th, 2026¶
On Monday, we discussed Pythagorean triples and the numpy module.
Primitive Pythagorean triples¶
It will be useful for the project to consider what are called, "primitive Pythagorean triples". We say that a Pythagorean triple $(a,b,c)$ is primitive if the greatest common divisor of $a$, $b$, and $c$ is $1$.
Math exercise: A Pythagorean triple $(a,b,c)$ is primitive if and only if the greatest common divisor of $a$ and $b$ is $1$.
Recall from the project page that a pair of integers $(a,b)$ is a Pythagorean double if there as integer $c$ such that $(a,b,c)$ is a Pythagorean triple. In light of the math exercise above, we will call a Pythagorean double $(a,b)$ primitive if $a$ and $b$ have greatest common divisor $1$.
In addition to plotting all Pythagorean doubles in a given range, it will be useful to also plot only the primitive Pythagorean doubles. To that end, can we calculated greatest common divisors? Let's write our own greatest_common_divisor function.
def greatest_common_divisor(a,b):
for d in range(min(abs(a),abs(b)), 0, -1):
if (a % d == 0) and (b % d == 0):
return d
greatest_common_divisor(10,15)
5
greatest_common_divisor(99,54)
9
greatest_common_divisor(10,-15)
5
Another method of calculating greatest common divisors is known as the Euclidean algorithm. The algorithm is based on the following facts.
- If $d$ divides both $a$ and $b$, then $d$ must also divide $b - a$.
- If $d$ divides both $a$ and $b-a$, then $d$ must also divide $b$.
Putting these together, we see that the greatest common divisor of $a$ and $b$ is exactly the same as the greatest common divisor of $a$ and $b-a$. The algorithm then proceeds as follows.
- Start with integers $a$ and $b$, and assume that $a \leq b$.
- Subtract the smaller $a$ from the larger $b$, and replace the larger $b$ with the difference $b - a$.
- Repeat step 2 until one of the numbers is $0$. The other will be the greatest common divisor.
Exercise: Write a function to implement the Euclidean algorithm
def Euclidean_algorithm(a,b):
a = abs(a)
b = abs(b)
while (a > 0) and (b > 0):
if a > b:
a,b = b,a
b = b % a
return a
Euclidean_algorithm(10,15)
5
Euclidean_algorithm(99,54)
9
Euclidean_algorithm(-10,15)
5
import time
t0 = time.time()
print(greatest_common_divisor(10**6*455, 10**6*455 + 1))
t1 = time.time()
print(t1 - t0,'seconds')
1 72.49251222610474 seconds
t0 = time.time()
print(Euclidean_algorithm(10**6*455, 10**6*455 + 1))
t1 = time.time()
print(t1 - t0,'seconds')
1 0.0008251667022705078 seconds
Exercise: Generate a list of Pythagorean triples using the get_ptriples function. Then generate a slice consisting of only the primitive Pythagorean triples (use list comprehension if you can).
ptriples = [[3, 4, 5],
[4, 3, 5],
[5, 12, 13],
[6, 8, 10],
[8, 6, 10],
[8, 15, 17],
[9, 12, 15],
[12, 5, 13],
[12, 9, 15],
[12, 16, 20],
[15, 8, 17],
[15, 20, 25],
[16, 12, 20],
[20, 15, 25]]
for ptriple in ptriples[:10]:
print(ptriple)
[3, 4, 5] [4, 3, 5] [5, 12, 13] [6, 8, 10] [8, 6, 10] [8, 15, 17] [9, 12, 15] [12, 5, 13] [12, 9, 15] [12, 16, 20]
a = 6
b = 8
c = 10
gcd_ab = Euclidean_algorithm(a,b)
gcd = Euclidean_algorithm(gcd_ab, c)
print(gcd)
2
def is_primitive(ptriple):
Figures and axes with matplotlib¶
We have been using matplotlib.pyplot to generate plots in Python. Up to now, we've mostly let matplotlib choose most of the formatting options automatically when generating the figure. Consider the following example where this can give undesireable results.
Suppose we want to draw the unit circle (i.e. the circle of radius $1$ centered at the origin). Recall from MTH 142 that we can describe the unit circle by the parametric equation $$x = \cos t \qquad\qquad y = \sin t \qquad \qquad 0 \leq t \leq 2\pi.$$
We can use np.linspace, np.pi, np.sin, and np.cos to plot the unit circle using the parametrization above.
import matplotlib.pyplot as plt
import numpy as np
t = np.linspace(0, 2*np.pi, 1000)
x = np.cos(t)
y = np.sin(t)
plt.plot(x,y)
[<matplotlib.lines.Line2D at 0x29d4e546710>]
Notice: the graph does not look circular. We need to adjust the aspect ratio (i.e. the horizontal:vertical ratio) to correct this. First, a little more information on the structure of figures in matplotlib. When plotting with matplotlib.pyplot:
- Each figure starts with a
figureobject. - Within that figure object, we can place an
axesobject (or several). - On an
axesobject, we can draw things (lines, points, etc.).
When we call plt.plot (in the absence of other surrounding commands), matplotlib automatically generates a figure and axes object. We can also take control of that process ourselves:
- Calling
plt.figure()will generate a figure object.
Exercise: Create two figures, one which graphs $x$ vs $y$ (as in the code above), and another that graphs both $x(t)$ and $y(t)$ vs $t$.
plt.figure()
plt.plot(x,y)
plt.title('$x$ vs $y$')
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.figure()
plt.plot(t,x, label='$x(t)$')
plt.plot(t,y, label='$y(t)$')
plt.xlabel('$t$')
plt.legend()
<matplotlib.legend.Legend at 0x29d500982f0>
By defining the figure object ourselves, we are able to take more control and can include numerous optional arguments to change the default behavior. For example, the optional argument figsize=(<horizontal size>, <vertical size>) can be used to change the size of the figure. Let's graph the circle again, but this time specify a figure size that is square.
plt.figure(figsize=(4,4))
plt.plot(x,y)
plt.title('$x$ vs $y$')
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.figure(figsize=(4,2))
plt.plot(t,x, label='$x(t)$')
plt.plot(t,y, label='$y(t)$')
plt.xlabel('$t$')
plt.legend()
<matplotlib.legend.Legend at 0x29d506d2ad0>
Now suppose we want to graph a second circle centered at $(3,0)$.
plt.figure(figsize=(4,4))
plt.plot(x,y)
plt.plot(x + 3, y)
[<matplotlib.lines.Line2D at 0x29d51b48550>]
We see that neither circle is circular. Unfortunately, using the figure size is not a robust way to set aspect ratios. Instead, we can take control of the aspect ratio through the axes object. To do so, we will need to add an axes object ourselves. Calling the plt.subplot function is one mechanism for adding an axes object to a figure.
The plt.subplot function can take in an optional argument, aspect. We can use aspect='equal' to force an equal aspect ratio.
Note: by default, matplotlib will shrink the figure to (approximately) the extents of the data. As a result, when forcing an equal aspect ratio, the resulting figure may end up smaller than the specified figsize.
plt.figure(figsize=(12,4))
plt.subplot(aspect='equal')
plt.plot(x,y)
plt.plot(x + 3,y)
plt.xlim(-4, 10)
(-4.0, 10.0)