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
Each triangle can be tested independently by looking only at orientation signs. For vertices , , and , the quantities , , and are the signed areas of the triangles formed with the origin. If these three values are all nonnegative or all nonpositive, then the origin lies inside the triangle (or on its boundary); if both positive and negative signs occur, the origin must lie outside.
That reduces the problem to a straight scan of the data file. For every triangle we compute three determinants, apply the sign test, and increment the answer when the origin passes the test.
Pseudocode
Set the count to zero.
For each triangle with vertices A, B, and C:
Compute the signed areas of OAB, OBC, and OCA.
If these three values do not mix positive and negative signs:
Increase the count.
Return the 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