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,...
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 , , in , define the signed area function:
Equivalently, .
Theorem 1 (Point-in-Triangle via Signed Areas). Let be the vertices of a non-degenerate triangle and let be a point in . Then lies in the interior of if and only if , , and all have the same nonzero sign.
Proof. The directed edge partitions into two open half-planes, where is the line through and . The sign of determines in which half-plane lies: positive if is to the left of (counterclockwise side), negative if to the right.
If has counterclockwise orientation (), then the interior equals the intersection of the three left half-planes defined by , , . Hence is interior if and only if , , and .
If has clockwise orientation (), the interior is the intersection of the three right half-planes, and all three signed areas are negative. In both cases, is interior if and only if all three signed areas share the same nonzero sign.
Lemma 1 (Simplification for ). When is the origin, the half-plane tests reduce to cross products of position vectors:
Proof. Substituting into the signed area formula:
which is the -component of .
Corollary. The origin lies inside if and only if the three quantities , , and are all strictly positive or all strictly negative.
Proof. Immediate from Theorem 1 and Lemma 1, noting that multiplication by 2 preserves sign.
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. where is the number of triangles. Each triangle requires three cross-product computations and three comparisons, all .
- Space. auxiliary. Each triangle is processed independently.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#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;
}
"""
Problem 102: Triangle Containment
Count how many of 1000 triangles contain the origin (0,0).
Uses the cross-product sign test.
"""
import os
import urllib.request
def contains_origin(x1, y1, x2, y2, x3, y3):
d1 = x1 * y2 - x2 * y1
d2 = x2 * y3 - x3 * y2
d3 = x3 * y1 - x1 * y3
has_neg = (d1 < 0) or (d2 < 0) or (d3 < 0)
has_pos = (d1 > 0) or (d2 > 0) or (d3 > 0)
return not (has_neg and has_pos)
def get_data():
local_file = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"p102_triangles.txt")
if os.path.exists(local_file):
with open(local_file) as f:
return f.read()
url = "https://projecteuler.net/resources/documents/0102_triangles.txt"
response = urllib.request.urlopen(url)
data = response.read().decode("utf-8")
with open(local_file, "w") as f:
f.write(data)
return data
def solve():
data = get_data()
count = 0
for line in data.strip().split("\n"):
x1, y1, x2, y2, x3, y3 = map(int, line.strip().split(","))
if contains_origin(x1, y1, x2, y2, x3, y3):
count += 1
return count
if __name__ == "__main__":
answer = solve()
print(answer)
assert answer == 228