Happy Ending Convex n-gon Problem

A Manim Animation of the Erdős–Szekeres Theorem

Understanding the Problem

The "happy ending problem" (named by Erdős because it led to the marriage of George Szekeres and Esther Klein) asks:

"For any integer n ≥ 3, what is the smallest number N(n) such that any set of N(n) points in the plane, no three collinear, contains a subset of n points that form the vertices of a convex n-gon?"

The Erdős–Szekeres theorem states that for any positive integer n, there exists a smallest integer N(n) such that any set of N(n) points in general position contains a convex n-gon.

This would show our Manim animation illustrating the theorem
with gradually increasing points forming convex polygons

Manim Implementation

1. Setup the Scene

from manim import * class HappyEndingProblem(Scene): def construct(self): # Title setup title = Tex("Happy Ending Problem").scale(1.5) subtitle = Tex("The Erdős–Szekeres Theorem").scale(0.8) subtitle.next_to(title, DOWN) self.play(Write(title), Write(subtitle)) self.wait(2) self.play(FadeOut(title), FadeOut(subtitle))

2. Creating Points and Showing Convex Hull

def show_convex_hull_evolution(self, n_points): # Create random points (in general position) points = self.generate_general_position_points(n_points) # Plot points dots = VGroup(*[Dot(point) for point in points]) labels = VGroup(*[ Text(f"{i+1}", font_size=24).next_to(dot, UP) for i, dot in enumerate(dots) ]) self.play(Create(dots), Create(labels)) # Show convex hull at different stages for k in range(3, min(n_points, 6) + 1): convex_hull = self.get_convex_hull(points[:k]) hull_polygon = Polygon(*points[:k], color=BLUE, fill_opacity=0.3) self.play( Create(hull_polygon), *[Indicate(dots[i]) for i in range(k)] ) # Label the convex hull hull_label = Tex(f"{k}-gon", color=BLUE).next_to(hull_polygon, UP) self.play(Write(hull_label)) self.wait(1) # Fade out for next iteration self.play( FadeOut(hull_polygon), FadeOut(hull_label) )

3. Helper Functions

def generate_general_position_points(self, n): """Generate points in general position (no three collinear)""" points = [] while len(points) < n: new_point = np.round(np.random.uniform(-4, 4, 2), 1) valid = True # Check for collinearity for i in range(len(points)): for j in range(i+1, len(points)): for k in range(j+1, len(points)): if self.are_collinear(points[i], points[j], points[k]): valid = False break if valid: points.append(new_point) return points def are_collinear(self, a, b, c): """Check if three points are collinear""" return abs((b[0]-a[0])*(c[1]-a[1]) - (b[1]-a[1])*(c[0]-a[0])) < 1e-6 def get_convex_hull(self, points): """Compute convex hull using Andrew's monotone chain algorithm""" if len(points) <= 1: return points # Sort points lexographically points = sorted(points, key=lambda x: [x[0], x[1]]) # Build lower hull lower = [] for p in points: while len(lower) >= 2 and self.cross(lower[-2], lower[-1], p) <= 0: lower.pop() lower.append(p) # Build upper hull upper = [] for p in reversed(points): while len(upper) >= 2 and self.cross(upper[-2], upper[-1], p) <= 0: upper.pop() upper.append(p) return lower[:-1] + upper[:-1] def cross(self, o, a, b): return (a[0]-o[0])*(b[1]-o[1]) - (a[1]-o[1])*(b[0]-o[0])

Complete Animation Script

from manim import * import numpy as np class HappyEndingProblem(Scene): def construct(self): # Title scene self.show_title() # Explanation of the problem self.show_problem_explanation() # Case study with 5 points self.show_case_study_n5() # General theorem explanation self.show_general_theorem() # Conclusion self.show_conclusion() def show_title(self): title = Tex("Happy Ending Problem").scale(1.5) subtitle = Tex("The Erdős–Szekeres Theorem").scale(0.9) subtitle.next_to(title, DOWN) self.play( Write(title), FadeIn(subtitle, shift=UP) ) self.wait(2) self.play( FadeOut(title, shift=UP), FadeOut(subtitle, shift=DOWN) ) def show_problem_explanation(self): # Problem statement problem = Text("For any n ≥ 3, find the smallest N(n) such that").scale(0.8) continuation = Text("any N(n) points in general position contain").scale(0.8) conclusion = Text("a convex n-gon.").scale(0.8) continuation.next_to(problem, DOWN, aligned_edge=LEFT) conclusion.next_to(continuation, DOWN, aligned_edge=LEFT) group = VGroup(problem, continuation, conclusion).center() # Show problem statement self.play(Write(group)) self.wait(3) # Show minimal cases minimal = Tex("N(3)=3,\\ N(4)=5,\\ N(5)=9,\\ \\dots").scale(1.2) minimal.next_to(group, DOWN, buff=1) self.play(FadeIn(minimal, shift=UP)) self.wait(2) # Clear the scene self.play(*[FadeOut(mob) for mob in self.mobjects]) def show_case_study_n5(self): # Create 9 points in general position points = self.generate_general_position_points(9) dots = VGroup(*[Dot(point) for point in points]) # Show all points self.play(Create(dots)) self.wait() # Animate through subsets sequences = [ [0, 1, 2, 3, 6], # Shows a quad + point without forming 5-gon [0, 1, 2, 3, 4], # Forms convex 5-gon [0, 2, 4, 6, 8] # Another convex 5-gon ] for seq in sequences: selected_points = [points[i] for i in seq] # Highlight selected points animations = [] for i in seq: animations.append(dots[i].animate.set_color(YELLOW)) self.play(*animations) self.wait(0.5) # Show convex hull polygon = Polygon(*selected_points, color=BLUE, fill_opacity=0.3) label = Tex("5-gon", color=BLUE).next_to(polygon, UP) self.play(Create(polygon), Write(label)) self.wait(2) # Clean up for next iteration self.play( Uncreate(polygon), FadeOut(label), *[dots[i].animate.set_color(WHITE) for i in seq] ) # Clear for next section self.play(FadeOut(dots)) def show_general_theorem(self): # Theorem statement theorem = Tex( "\\text{For any integer } n \\geq 3,\\text{ there exists a smallest}\\\\" + "N(n)\\text{ such that any } N(n)\\text{ points in general position}\\\\" + "\\text{contain a convex } n\\text{-gon.}", tex_to_color_map={"N(n)": BLUE, "n": YELLOW} ).scale(0.9) bound = Tex( "N(n) \\leq \\binom{2n-5}{n-3} + 1 \\approx 4^{n}", tex_to_color_map={"N(n)": BLUE} ).next_to(theorem, DOWN) self.play(Write(theorem)) self.wait(3) # Show bound self.play(Write(bound)) self.wait(3) # Show known values known = VGroup( Tex("N(3) = 3"), Tex("N(4) = 5"), Tex("N(5) = 9"), Tex("N(6) = 17") ).arrange(RIGHT, buff=1).next_to(bound, DOWN, buff=1) self.play(LaggedStart(*[Write(k) for k in known], lag_ratio=0.5)) self.wait(2) # Clear for conclusion self.play(*[FadeOut(mob) for mob in self.mobjects]) def show_conclusion(self): # Happy ending story story = Text("The problem is named after its happy ending:").scale(0.7) marriage = Text("It led to the marriage of Esther Klein and George Szekeres", color=YELLOW).scale(0.7) marriage.next_to(story, DOWN) # Final thought final = Text("Open problem: Exact values of N(n) for n ≥ 6 remain unknown", color=BLUE).scale(0.7) final.next_to(marriage, DOWN, buff=1) self.play(Write(story)) self.play(FadeIn(marriage, shift=UP)) self.wait(2) self.play(FadeIn(final, shift=UP)) self.wait(3) # Helper functions from previous example def generate_general_position_points(self, n): points = [] while len(points) < n: new_point = np.round(np.random.uniform(-5, 5, 2), 1) if new_point[0] in [-5, 5]: new_point[0] += np.random.choice([-0.5, 0.5]) if new_point[1] in [-5, 5]: new_point[1] += np.random.choice([-0.5, 0.5]) valid = True for i in range(len(points)): for j in range(i+1, len(points)): for k in range(j+1, len(points)): if self.are_collinear(points[i], points[j], points[k]): valid = False break if valid and not any(np.array_equal(new_point, p) for p in points): points.append(new_point) return points def are_collinear(self, a, b, c): return abs((b[0]-a[0])*(c[1]-a[1]) - (b[1]-a[1])*(c[0]-a[0])) < 1e-6
Note: This complete script includes title sequences, problem explanation, case study with N(5)=9, general theorem statement, and the historical "happy ending" conclusion.

Running the Animation

$ pip install manim
$ manim -pql happy_ending.py HappyEndingProblem
$ # For higher quality:
$ manim -pqh happy_ending.py HappyEndingProblem

Expected Output Phases:

1

Title Sequence

The animation starts with the title "Happy Ending Problem" and the subtitle "The Erdős–Szekeres Theorem" appearing elegantly.

2

Problem Explanation

Text appears explaining the problem statement formally, then showing the minimal known cases N(3)=3, N(4)=5, etc.

3

Case Study (N=5)

The animation shows 9 points forming different subsets, highlighting how convex 5-gons appear among them.

4

General Theorem

The general theorem statement appears with the upper bound formula, followed by known values of N(n).

5

Happy Ending

The animation concludes with the historical context of how this problem led to the marriage of Esther Klein and George Szekeres.

Animation Enhancements

1

Improved Visualization

To make the convex hull visualization more impressive:

# Add to your polygon creation polygon.set_fill(BLUE, opacity=0.3) polygon.set_stroke(width=3) # Add pulsing effect to the convex hull self.play( polygon.animate.set_fill(opacity=0.5), rate_func=there_and_back )
2

Counting Animation

Show the combinatorial counting involved:

# When showing the bound counting_animation = Counter( "Combinations:", start=0, end=comb(2*n-5, n-3)+1, position=UP, wait_until_completion=True ) self.play(counting_animation.create())
3

Historical Images

Include images of the mathematicians:

# At the conclusion klein = ImageMobject("esther_klein.png").scale(0.5) szekeres = ImageMobject("george_szekeres.png").scale(0.5) klein.to_edge(LEFT) szekeres.to_edge(RIGHT) self.play(FadeIn(klein), FadeIn(szekeres))

Additional Resources

This website has been generated by DeepSite DeepSite Logo