All Euler problems
Project Euler

Triangle Containment

Three distinct points are plotted at random on a Cartesian plane, for which -1000 <= x, y <= 1000, such that a triangle is formed. Given a file containing the coordinates of one thousand triangles,...

Source sync Apr 19, 2026
Problem #0102
Level Level 03
Solved By 24,602
Languages C++, Python
Answer 228
Length 339 words
geometryprobability

Problem Statement

This archive keeps the full statement, math, and original media on the page.

Three distinct points are plotted at random on a Cartesian plane, for which \(-1000 \le x, y \le 1000\), such that a triangle is formed.

Consider the following two triangles: \begin {gather*} A(-340,495), B(-153,-910), C(835,-947)\\ X(-175,41), Y(-421,-714), Z(574,-645) \end {gather*} It can be verified that triangle \(ABC\) contains the origin, whereas triangle \(XYZ\) does not.

Using triangles.txt (right click and ’Save Link/Target As...’), a 27K text file containing the co-ordinates of one thousand "random" triangles, find the number of triangles for which the interior contains the origin.

NOTE: The first two examples in the file represent the triangles in the example given above.

Problem 102: Triangle Containment

Mathematical Development

Definition. For points U=(ux,uy)U = (u_x, u_y), V=(vx,vy)V = (v_x, v_y), W=(wx,wy)W = (w_x, w_y) in R2\mathbb{R}^2, define the signed area function:

σ(U,V,W)=12[(vxux)(wyuy)(wxux)(vyuy)].\sigma(U, V, W) = \frac{1}{2}\bigl[(v_x - u_x)(w_y - u_y) - (w_x - u_x)(v_y - u_y)\bigr].

Equivalently, σ(U,V,W)=12det ⁣(vxuxwxuxvyuywyuy)\sigma(U, V, W) = \frac{1}{2}\det\!\begin{pmatrix} v_x - u_x & w_x - u_x \\ v_y - u_y & w_y - u_y \end{pmatrix}.

Theorem 1 (Point-in-Triangle via Signed Areas). Let A,B,CA, B, C be the vertices of a non-degenerate triangle and let PP be a point in R2\mathbb{R}^2. Then PP lies in the interior of ABC\triangle ABC if and only if σ(A,B,P)\sigma(A, B, P), σ(B,C,P)\sigma(B, C, P), and σ(C,A,P)\sigma(C, A, P) all have the same nonzero sign.

Proof. The directed edge AB\overrightarrow{AB} partitions R2AB\mathbb{R}^2 \setminus \ell_{AB} into two open half-planes, where AB\ell_{AB} is the line through AA and BB. The sign of σ(A,B,P)\sigma(A, B, P) determines in which half-plane PP lies: positive if PP is to the left of AB\overrightarrow{AB} (counterclockwise side), negative if to the right.

If ABC\triangle ABC has counterclockwise orientation (σ(A,B,C)>0\sigma(A, B, C) > 0), then the interior equals the intersection of the three left half-planes defined by AB\overrightarrow{AB}, BC\overrightarrow{BC}, CA\overrightarrow{CA}. Hence PP is interior if and only if σ(A,B,P)>0\sigma(A, B, P) > 0, σ(B,C,P)>0\sigma(B, C, P) > 0, and σ(C,A,P)>0\sigma(C, A, P) > 0.

If ABC\triangle ABC has clockwise orientation (σ(A,B,C)<0\sigma(A, B, C) < 0), the interior is the intersection of the three right half-planes, and all three signed areas are negative. In both cases, PP is interior if and only if all three signed areas share the same nonzero sign. \blacksquare

Lemma 1 (Simplification for P=OP = O). When P=(0,0)P = (0, 0) is the origin, the half-plane tests reduce to cross products of position vectors:

2σ(A,B,O)=axbybxay=(OA×OB)z.2\,\sigma(A, B, O) = a_x b_y - b_x a_y = (\vec{OA} \times \vec{OB})_z.

Proof. Substituting P=(0,0)P = (0, 0) into the signed area formula:

2σ(A,B,O)=(bxax)(0ay)(0ax)(byay)=bxay+axay+axbyaxay=axbybxay,\begin{align*} 2\,\sigma(A, B, O) &= (b_x - a_x)(0 - a_y) - (0 - a_x)(b_y - a_y) \\ &= -b_x a_y + a_x a_y + a_x b_y - a_x a_y \\ &= a_x b_y - b_x a_y, \end{align*}

which is the zz-component of OA×OB\vec{OA} \times \vec{OB}. \blacksquare

Corollary. The origin lies inside ABC\triangle ABC if and only if the three quantities axbybxaya_x b_y - b_x a_y, bxcycxbyb_x c_y - c_x b_y, and cxayaxcyc_x a_y - a_x c_y are all strictly positive or all strictly negative.

Proof. Immediate from Theorem 1 and Lemma 1, noting that multiplication by 2 preserves sign. \blacksquare

Editorial

Count how many of 1000 triangles contain the origin (0,0). Uses the cross-product sign test. We enumerate the admissible parameter range, discard candidates that violate the derived bounds or arithmetic constraints, and update the final set or total whenever a candidate passes the acceptance test.

Pseudocode

    Set count <- 0
    For each each (A, B, C) in triangles:
        Set d1 <- A.x * B.y - B.x * A.y
        Set d2 <- B.x * C.y - C.x * B.y
        Set d3 <- C.x * A.y - A.x * C.y
        Set has_neg <- (d1 < 0) or (d2 < 0) or (d3 < 0)
        Set has_pos <- (d1 > 0) or (d2 > 0) or (d3 > 0)
        If not (has_neg and has_pos) then
            Set count <- count + 1
    Return count

Complexity Analysis

  • Time. O(N)O(N) where N=1000N = 1000 is the number of triangles. Each triangle requires three cross-product computations and three comparisons, all O(1)O(1).
  • Space. O(1)O(1) auxiliary. Each triangle is processed independently.

Answer

228\boxed{228}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_102/solution.cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <algorithm>
using namespace std;

/*
 * Problem 102: Triangle Containment
 *
 * Test whether the origin lies inside each triangle using the
 * cross-product sign method: compute the z-components of
 * OA x OB, OB x OC, OC x OA and check that all share the same sign.
 */

bool contains_origin(int x1, int y1, int x2, int y2, int x3, int y3) {
    long long d1 = (long long)x1 * y2 - (long long)x2 * y1;
    long long d2 = (long long)x2 * y3 - (long long)x3 * y2;
    long long d3 = (long long)x3 * y1 - (long long)x1 * y3;
    bool has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
    bool has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
    return !(has_neg && has_pos);
}

int main() {
    ifstream fin("p102_triangles.txt");
    istream& in = fin.is_open() ? fin : cin;

    int count = 0;
    string line;
    while (getline(in, line)) {
        if (line.empty()) continue;
        replace(line.begin(), line.end(), ',', ' ');
        istringstream iss(line);
        int x1, y1, x2, y2, x3, y3;
        if (iss >> x1 >> y1 >> x2 >> y2 >> x3 >> y3) {
            if (contains_origin(x1, y1, x2, y2, x3, y3))
                count++;
        }
    }

    cout << count << endl;
    return 0;
}