tgen::miniblog(7): random simple polygons

2026-10-22 · Originally published on Codeforces

This is blog 7 of a series of blogs about algorithmic challenges I came across when creating tgen.

In this blog we will tackle:

  1. Given nn distinct points that are not all collinear, order them as the vertices of a random simple polygon, in O(nlog⁡n)\mathcal O(n\log n) expected time.

Animation of a simple polygon being built by recursively splitting the points

Building a simple polygon by recursively splitting the points.

A polygon is simple if its boundary does not intersect itself. A random permutation of the points usually has many crossings, and repeatedly removing crossings by local fixes can be slow. Instead, we will use divide and conquer to construct a noncrossing Hamiltonian path, then use this construction to build the polygon. I first saw this idea in this Codeforces comment.

The result is not uniformly random among all simple polygons through the points. Random choices only give us varied polygon shapes.

Using paths to create a polygon

For now, assume the points are in general position, meaning no three are collinear.

We first need a smaller problem: given a set of points containing two distinguished endpoints AA and BB, construct a simple Hamiltonian path from AA to BB through every point in the set.

Suppose we can solve that problem. To build a polygon, take the lexicographically smallest point AA and largest point BB. Split every other point by which side of the directed line ABAB it lies on. Build one path from AA to BB through the points below ABAB, and another from BB to AA through the points above it.

Two boundary paths on opposite sides of line AB

The red path runs from AA to BB below the dashed line ABAB; the blue path returns from BB to AA above it.

Since the edges of the two paths stay on opposite sides of the dividing line, the paths meet only at their endpoints AA and BB. Together they form a simple polygon.

The Divide-and-Conquer Algorithm

Given a set of points and two designated endpoints AA and BB, we want a noncrossing path from AA to BB that visits every point exactly once. In later recursive calls, the points may lie on both sides of ABAB. If there is at most one other point, the path is immediate. Otherwise:

  1. Choose a pivot CC uniformly among the points different from AA and BB.
  2. Independently choose a point DD uniformly from the interior of segment ABAB.
  3. The line CDCD separates AA from BB. Put every remaining point on the same side as AA or on the same side as BB.

Points partitioned by the separating line CD

A recursive subproblem: blue points lie on the AA-side of the dashed line CDCD, red points on the BB-side. The hollow point DD lies on the dotted segment ABAB and is not an input point.

Recursively build the path from AA to CC through the first set, and the path from CC to BB through the second set. Concatenate them at CC to obtain the required AA-BB path.

Recursive paths AC and CB joined at pivot C

The same points after recursion. The blue AA-CC path and red CC-BB path stay on opposite sides of CDCD and meet only at CC.

There are only finitely many positions of DD for which CDCD passes through another input point, so a continuous random choice avoids all of them with probability 11.

A simple polygon through 42 points, formed by two colored boundary paths

A generated polygon through 42 points. The red AA-BB path and blue BB-AA path form its boundary; every input point is a vertex.

Collinear points

We can also polygonize any set of at least three distinct points, as long as they are not all collinear. Consecutive edges may be collinear, but crossings and overlapping edges are still forbidden.

The construction extends to collinear inputs with some additional care; we omit those details here.

A simple polygon through all 49 points of a seven-by-seven lattice

A polygonization of all 49 points of a 7×77\times7 lattice. Collinear vertices are allowed; each point appears exactly once on the boundary.

Complexity

We analyze the version above with points in general position and continuous choices of DD. The input points can be placed arbitrarily; the expectation is over the algorithm’s random choices.

Lemma 1: In a call with mm non-endpoint points, with probability at least 1/61/6, each child has at most 5m/65m/6 non-endpoint points.

Proof: Fix DD, excluding the finitely many positions that make it collinear with two non-endpoint points. Let HH be the points on the side of ABAB containing the most points (choose either side in a tie). Then k=∣H∣≥m/2k=|H|\ge m/2. Order these points by angle around DD.

Because DD lies on ABAB, these directions lie in an open semicircle. If CC is the jj-th point in this order, CDCD leaves j−1j-1 points of HH on one side and k−jk-j on the other.

Nine points in angular order around D, with pivot C separating four earlier points from four later points

The nine points above ABAB form HH, numbered in angular order around DD; the gray points are outside HH. Here CC has rank 55, so CDCD separates four blue points from four red points. Gold rings mark ranks 4,5,64,5,6: choosing any of these pivots leaves at least three points of HH on each side.

Consider pivots whose ranks in HH are

⌊k/3⌋+1,…,k−⌊k/3⌋.\lfloor k/3\rfloor+1,\ldots,k-\lfloor k/3\rfloor.

There are at least k/3≥m/6k/3\ge m/6 such pivots, so a uniform choice among all mm candidates selects one with probability at least 1/61/6. Each side of CDCD then contains at least ⌊k/3⌋\lfloor k/3\rfloor points. Since CC becomes an endpoint of both children, either child has at most

m−1−⌊k/3⌋≤m−1−⌊m/6⌋≤5m6m-1-\lfloor k/3\rfloor \le m-1-\lfloor m/6\rfloor \le \frac{5m}{6}

non-endpoint points. This holds for every admissible fixed DD, hence also for an independent continuous random choice of DD.

□\square

Theorem 1: For every input in general position, this version runs in O(nlog⁡n)\mathcal O(n\log n) expected time.

Proof: Let XPX_P count the non-base calls in which point PP is a non-endpoint. Call a split balanced if both children have at most 5/65/6 of the parent’s non-endpoint points. Since PP goes to only one side of the recursion, after at most ⌈log⁡6/5n⌉\lceil\log_{6/5}n\rceil balanced splits it reaches a base case. By Lemma 1, even conditioned on previous choices, the expected wait for each balanced split (or for PP‘s participation to end) is at most 66 calls. Hence

E[XP]≤6⌈log⁡6/5n⌉.\mathbb E[X_P]\le 6\lceil\log_{6/5}n\rceil.

Charge each non-base call’s linear work equally to its non-endpoint points. The initial split and all base calls cost O(n)\mathcal O(n), so the total running time TT satisfies

E[T]=O(n+∑PE[XP])=O(n+∑Plog⁡n)=O(nlog⁡n).\mathbb E[T] =\mathcal O\left(n+\sum_P\mathbb E[X_P]\right) =\mathcal O\left(n+\sum_P\log n\right) =\mathcal O(n\log n).

□\square

The expected O(nlog⁡n)\mathcal O(n\log n) bound also extends to collinear inputs. We omit the additional case analysis to keep the proof short. An unlucky sequence of unbalanced splits can still take Θ(n2)\Theta(n^2) time.

The output distribution is not uniform, and the divide-and-conquer structure may bias which polygon shapes appear. As with any specialized generator, it is best combined with other test families when the polygon shape can affect the difficulty of the problem.

References

Thomas Auer and Martin Held, Heuristics for the Generation of Random Polygons, CCCG 1996, pp. 38–43. See “Space Partitioning” in Section 2.2, page 40.

Codeforces explanation by TLE