--- jupyter: description: 'The isoperimetric inequality with planex2d: the isoperimetric quotient 4πA/L² of a circle, a Reuleaux triangle, an ellipse, regular polygons and a square, each against its exact value, and Dido''s problem along a straight coast.' --- # The isoperimetric inequality Of all closed curves of a given length, which one encloses the most area? The circle, and the isoperimetric inequality says by how much: for any curve of length $L$ around an area $A$, $$4\pi A \le L^2,$$ with equality only for the circle. The isoperimetric quotient $Q = 4\pi A / L^2$ is therefore 1 for a circle and less for every other shape, and it measures how round a shape is. Here, planex2d measures the area and the length of several shapes exactly, and their quotients are checked against the closed forms. ![Five shapes of the same perimeter, colored by their isoperimetric quotient](../_static/gallery/isoperimetric-inequality.png){.result} ```python import math import planex as px from scipy.special import ellipe ``` ```python nbsphinx="hidden" from docs_figures import Chart, Row ``` ## Shapes of one perimeter The quotient does not depend on a shape's size, so each shape is drawn at a convenient one. The shapes are a circle of radius 1, a Reuleaux triangle of width 1 from three arcs about the corners of a triangle, an ellipse of semi-axes 2 and 1, a square and an equilateral triangle. Their exact quotients are - $2(\pi - \sqrt{3})/\pi$ for the Reuleaux triangle, of area $(\pi - \sqrt{3})/2$ and length $\pi$; - $4\pi^2 a b / L^2$ for the ellipse, of length $L = 4a\,E(1 - b^2/a^2)$, with $E$ the complete elliptic integral of the second kind; - $\pi / (n \tan(\pi/n))$ for a regular polygon of $n$ sides, $\pi/4$ for the square. ```python corners = [(0.0, 0.0), (1.0, 0.0), (0.5, math.sqrt(3) / 2)] reuleaux = px.QuickSketch(corners[1]).Arc(center=corners[0], sweep=60).Arc(center=corners[1], sweep=60) reuleaux.Arc(center=corners[2], sweep=60) shapes = { "circle": (px.Circle((0, 0), 1).Face(), 1), "Reuleaux triangle": (reuleaux.Face(), 2 * (math.pi - math.sqrt(3)) / math.pi), "ellipse": (px.Face([px.Ellipse((0, 0), 2, 1)]), 4 * math.pi**2 * 2 / (4 * 2 * ellipe(1 - 1 / 4)) ** 2), "square": (px.QuickSketch().RectangleC(1, 1).Face(), math.pi / 4), "triangle": (px.QuickSketch().Polygon((0, 0), 1, 3).Face(), math.pi / (3 * math.tan(math.pi / 3))), } for name, (face, exact) in shapes.items(): print(name, "| Q", 4 * math.pi * face.mass / face.outer.mass**2, "| exact", exact) ``` ```python nbsphinx="hide-input" Row(*[(face, f"{name.capitalize()}, Q = {4 * math.pi * face.mass / face.outer.mass**2:.3f}") for name, (face, exact) in shapes.items()], caption="Five shapes and their isoperimetric quotients") ``` ## Polygons toward the circle The more sides a regular polygon has, the closer it comes to the circle, and its quotient rises toward 1 (Figure 2): ```python nbsphinx="hide-input" sides = list(range(3, 25)) polygons = [px.QuickSketch().Polygon((0, 0), 1, n).Face() for n in sides] Chart("number of sides", "isoperimetric quotient", dots={"regular polygons": (sides, [4 * math.pi * f.mass / f.outer.mass**2 for f in polygons])}, lines={"π / (n tan(π/n))": (sides, [math.pi / (n * math.tan(math.pi / n)) for n in sides]), "the circle": ([3, 24], [1, 1])}, title="Regular polygons against the circle") ``` ## Dido's problem Dido's problem, named after the legend of Queen Dido's ox hide, asks for the most land along a straight coast. The coast closes the curve for free, and a rope of length $\ell$ then encloses the most area as a half circle, the mirror image of the isoperimetric circle, with the area $\ell^2 / (2\pi)$. A rectangle with the same rope, best as twice as long as it is deep, encloses only $\ell^2/8$: ```python rope = 100.0 r = rope / math.pi half = px.QuickSketch((-r, 0)).Line(2 * r).Turn(90).Arc(r, 180).Face() box = px.QuickSketch((-rope / 4, 0)).Rectangle(rope / 2, rope / 4).Face() print("half circle", half.mass, "| ℓ²/2π", rope**2 / (2 * math.pi), "| rope", half.outer.mass - 2 * r) print("rectangle", box.mass, "| ℓ²/8", rope**2 / 8, "| rope", box.outer.mass - rope / 2) ``` ```python nbsphinx="hidden" import sys sys.path.insert(0, "..") import matplotlib.pyplot as plt from matplotlib.cm import ScalarMappable from matplotlib.colors import Normalize, to_hex from docs_figures import INK, MAP from gallery import save_figure scale = Normalize(0.6, 1.0) fig, ax = plt.subplots(figsize=(6, 4)) for (h, v), (face, exact) in zip(((0, 3), (3, 3), (6, 3), (1.5, 0), (4.5, 0)), shapes.values()): center = face.Measure()["center"][:2] same = face.Scaled(center, 2 * math.pi / face.outer.mass).Translated((h - center[0], v - center[1])) q = 4 * math.pi * face.mass / face.outer.mass**2 same.Plot(ax=ax, face_col=to_hex(MAP.reversed()(scale(q))), line_col=INK, arc_col=INK, spline_col=INK, legend=False) ax.set_aspect("equal", adjustable="box") ax.set_xlim(-1.5, 7.5) ax.set_ylim(-1.5, 4.5) fig.colorbar(ScalarMappable(scale, MAP.reversed()), ax=ax, label="4πA/L²", fraction=0.04, pad=0.02) save_figure(fig, "isoperimetric-inequality", bare=True, bars={1: "4πA/L²"}) ```