Convex Hull

11 Oct 2023


§ Statement

Problem

Given points P={Pi}i=1nE2\mathcal{P} = \{P_i\}_{i=1}^n \subset \mathbb{E}^2, find the smallest convex polygon Conv(P)\mathrm{Conv}(\mathcal{P}) such that PConv(P)\mathcal{P} \subseteq \mathrm{Conv}(\mathcal{P}).

§ Solution

First we pick a reference point Q0Q_0, in Graham's scan we pick the PiP_i with the smallest yy coordinate, then the smallest xx coordinate in case of a tie.

Then, we can treat each PiPP_i \in \mathcal{P} as a vector starting at the origin, and compute its angle θ\theta with respect to Q0Q_0.

θi=arccos[PiQ0PiQ0].\begin{align*} \theta_i &= \arccos \left[\frac{P_i \cdot Q_0}{\|P_i\| \|Q_0\|}\right]. \end{align*}

We need not compute the actual angle θ\theta; any function which is monotonically increasing over [0,π][0, \pi]. Incidentally, the cosine of the angle satisfies this property, hence we can let θ=P^iQ^0\theta = \hat{P}_i\cdot\hat{Q}_0.

Let Conv(P)\mathrm{Conv}(P) be the empty set, and QQ be the set P\mathcal{P} ordered by θi\theta_i, let ties be sorted by the L2L_2 from the centroid of P.\mathcal{P}.

Next, we can iterate through each point QiQ_i of Q\mathcal{Q}.

In each iteration:

  • If Conv(P)\mathrm{Conv}(\mathcal{P}) has fewer than two points, then
    • insert QiQ_i to the end of the array and skip to next iteration.
  • Check if the triangle formed by QiQ_i and the last two points of Conv(P)\mathrm{Conv}(\mathcal{P}) has positive area (i.e. points turn in anticlockwise direction)
    • If so, add QiQ_i to the end of Conv(P)\mathrm{Conv}(\mathcal{P}).
    • Otherwise, remove the last element of Conv(P)\mathrm{Conv}(\mathcal{P}), and repeat above step until QiQ_i can be inserted.

Finally, return Conv(P)\mathrm{Conv}(\mathcal{P}) which is the set containing the points of the convex hull.

§ Algorithm

Convex Hull (Graham Scan)

  • # Input: Set of points P={Pi}i=0n1\mathcal{P} = \{P_i\}_{i=0}^{n-1} in arbitrary order
  • function ConvexHull(P\mathcal{P}, PcP_c)
    • Q0Q_0 \leftarrow argminPiPi[y]\mathrm{argmin}_{P_i} P_i[y] # Pick PiP_i with smallest yy coordinate.
    • QQ \leftarrow P\mathcal{P} sorted by θi=angle(Q0,Pi)\theta_i = \mathrm{angle}(Q_0, P_i)
    • Conv(P)\mathrm{Conv}(\mathcal{P}) \leftarrow \varnothing
    •  
    • for all QiQ_i in QQ do
      • if Conv(P)2\|\mathrm{Conv}(\mathcal{P})\| \leq 2 then
        • Conv(P)\mathrm{Conv}(\mathcal{P}) \leftarrow Conv(P){Qi}\mathrm{Conv}(\mathcal{P}) \cup \{Q_i\}
        • continue
      •  
      • mm \leftarrow Conv(P)\|\mathrm{Conv}(\mathcal{P})\|
      • while true
        • triangle_area \leftarrow area(QiConv(P)m2Conv(P)m1)\mathrm{area}(\triangle Q_i\mathrm{Conv}(\mathcal{P})_{m-2}\mathrm{Conv}(\mathcal{P})_{m-1})
        • if triangle_area >0> 0 then
          • Conv(P)\mathrm{Conv}(\mathcal{P}) \leftarrow Conv(P){Qi}\mathrm{Conv}(\mathcal{P}) \cup \{Q_i\}
          • break
        •  
        • Conv(P)\mathrm{Conv}(\mathcal{P}) \leftarrow Conv(P){Conv(P)m1}\mathrm{Conv}(\mathcal{P}) \setminus \{\mathrm{Conv}(\mathcal{P})_{m-1}\}
    •  
    • return Conv(P)\mathrm{Conv}(\mathcal{P})

Complexity

  • Finding Q0Q_0 and compuing the angles requires a single traversal of P\mathcal{P} taking O(n)\mathcal{O}(n) time.
  • Sorting takes O(nlogn)\mathcal{O}(n \log n) time.
  • Inserting into Conv(P)\mathrm{Conv}(\mathcal{P}) takes O(n)\mathcal{O}(n) time.
    • We iterate through each QiQQ_i \in Q once
    • If we cannot insert the point, we must backtrack by removing elements from Conv(P)\mathrm{Conv}(\mathcal{P}), at worst this inner loop is run n1n - 1 times in total, over all QiQ_i, which is O(n)\mathcal{O}(n)

Therefore, this algorithm takes O(nlogn)\mathcal{O}(n \log n) time.