Note
Go to the end to download the full example code or to run this example in your browser via JupyterLite.
The fundamental theorem of algebra, by winding numbers#
Gauss’s 1799 dissertation gave the first substantial proof that every polynomial of degree \(n\) has \(n\) complex roots. The modern topological proof counts windings: on a huge circle \(p(z) \approx z^n\) winds \(n\) times around 0, on a tiny circle about a non-root it winds 0 times, so as the circle grows the image curve must pass through 0 – and the argument principle says it does so exactly \(n\) times.
import matplotlib.pyplot as plt
import numpy as np
from mathematicskit.complex_analysis import argument_principle, circle_contour
coefficients = [1.0, -2.0, 3.0, 1.0, -4.0, 2.0] # degree 5
p = np.poly1d(coefficients)
print("roots:", np.round(np.roots(coefficients), 4))
roots: [ 0.8363+1.6276j 0.8363-1.6276j -1.0563+0.j 0.6918+0.2946j
0.6918-0.2946j]
Winding number of \(p(|z| = R)\) about 0 as R grows#
radii = [0.2, 0.6, 1.0, 1.4, 2.0, 5.0]
fig, axes = plt.subplots(2, 3, figsize=(11, 7))
for ax, R in zip(axes.ravel(), radii, strict=False):
contour = circle_contour(0.0, R)
image = p(contour.points(2000))
winding = argument_principle(p, contour)
inside = int(np.sum(np.abs(np.roots(coefficients)) < R))
print(f"R = {R:3.1f}: winding number {winding}, roots inside {inside}")
ax.plot(image.real, image.imag, lw=1)
ax.plot(0, 0, "r+", ms=12, mew=2)
ax.set_title(f"R = {R}: winds {winding} times")
ax.set_aspect("equal")
fig.suptitle("Image of |z| = R under a degree-5 polynomial")
fig.tight_layout()
plt.show()

R = 0.2: winding number 0, roots inside 0
R = 0.6: winding number 0, roots inside 0
R = 1.0: winding number 2, roots inside 2
R = 1.4: winding number 3, roots inside 3
R = 2.0: winding number 5, roots inside 5
R = 5.0: winding number 5, roots inside 5
Total running time of the script: (0 minutes 0.406 seconds)