Intersections
A segment is uniquely defined by its two endpoints. By considering two line segments in plane geometry there are three possibilities: the segments have zero points, exactly one point, or infinitely...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
A segment is uniquely defined by its two endpoints.
By considering two line segments in plane geometry there are three possibilities:
the segments have zero points, one point, or infinitely many points in common.
Moreover when two segments have exactly one point in common it might be the case that that common point is an endpoint of either one of the segments or of both. If a common point of two segments is not an endpoint of either of the segments it is an interior point of both segments.
We will call a common point \(T\) of two segments \(L_1\) and \(L_2\) a true intersection point of \(L_1\) and \(L_2\) if \(T\) is the only common point of \(L_1\) and \(L_2\) and \(T\) is an interior point of both segments.
Consider the three segments \(L_1\), \(L_2\), and \(L_3\):
-
\(L_1\): \((27, 44)\) to \((12, 32)\)
-
\(L_2\): \((46, 53)\) to \((17, 62)\)
-
\(L_3\): \((46, 70)\) to \((22, 40)\)
It can be verified that line segments \(L_2\) and \(L_3\) have a true intersection point. We note that as the one of the end points of \(L_3\): \((22,40)\) lies on \(L_1\) this is not considered to be a true point of intersection. \(L_1\) and \(L_2\) have no common point. So among the three line segments, we find one true intersection point.
Now let us do the same for \(5000\) line segments. To this end, we generate \(20000\) numbers using the so-called "Blum Blum Shub" pseudo-random number generator.
\begin {align*} s_0 &= 290797\\ s_{n + 1} &= s_n \times s_n \pmod {50515093}\\ t_n &= s_n \pmod {500} \end {align*}
To create each line segment, we use four consecutive numbers \(t_n\). That is, the first line segment is given by: \((t_1, t_2)\) to \((t_3, t_4)\).
The first four numbers computed according to the above generator should be: \(27\), \(144\), \(12\) and \(232\). The first segment would thus be \((27,144)\) to \((12,232)\).
How many distinct true intersection points are found among the \(5000\) line segments?
Problem 165: Intersections
Mathematical Foundation
Theorem 1 (Parametric intersection). Given two line segments and where , define:
If , the lines containing the segments intersect at the point . This point is an interior point of both segments if and only if and (strictly).
Proof. Points on segment are parameterized as for , and points on segment as for . Setting these equal and solving the linear system:
By Cramer’s rule, the determinant is (with appropriate sign convention), giving the formulas above. The intersection is interior to both segments when and are strictly between 0 and 1, excluding endpoints.
Lemma 1 (Exact arithmetic sufficiency). Since all coordinates , the numerators and denominator in the intersection formulas are integers bounded by . The intersection point can be represented exactly as with , using 64-bit integers.
Proof. The coordinates are integers in . Each term in is a product of two differences of coordinates, each bounded by 499 in absolute value. So . The numerator of the intersection point’s -coordinate is:
where is the numerator of . All quantities fit in 64-bit integers (products of at most three 499-bounded values: ).
Lemma 2 (Distinct point identification). Two intersection points and (in lowest terms with positive denominators) are identical if and only if , , and .
Proof. Rational numbers in lowest terms have a unique representation. By normalizing each coordinate pair to have and appropriately reduced, equality of the triple is equivalent to geometric equality.
Editorial
Count distinct interior intersection points of 5000 line segments generated by the Blum Blum Shub pseudo-random number generator. Uses exact rational arithmetic to identify distinct points. We generate segments from BBS generator. We then find all interior intersection points. Finally, check 0 < t < 1 and 0 < u < 1 (strictly).
Pseudocode
Generate segments from BBS generator
Find all interior intersection points
Check 0 < t < 1 and 0 < u < 1 (strictly)
else
Compute exact intersection point and normalize
Point = (x1*D + t_num*(x2-x1), y1*D + t_num*(y2-y1)) / D
Normalize: (px/D, py/D) -> reduce to lowest terms
Complexity Analysis
- Time: where . The number of pairs is . Each pair requires arithmetic and a hash-set insertion. Total: .
- Space: where is the number of distinct interior intersection points, for the hash set. In the worst case .
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
// Problem 165: Intersections
// Find distinct interior intersection points of 5000 line segments
// generated by the Blum Blum Shub generator.
typedef long long ll;
typedef __int128 lll;
struct Seg {
ll x1, y1, x2, y2;
};
int main() {
// Generate segments
const int N = 5000;
vector<Seg> segs(N);
ll s = 290797;
auto next_s = [&]() -> ll {
s = (ll)((lll)s * s % 50515093LL);
return s;
};
for (int i = 0; i < N; i++) {
ll t1 = next_s() % 500;
ll t2 = next_s() % 500;
ll t3 = next_s() % 500;
ll t4 = next_s() % 500;
segs[i] = {t1, t2, t3, t4};
}
// For each pair, find interior intersection
// Use exact rational arithmetic
// Store points as (px/d, py/d) reduced to lowest terms with d > 0
set<tuple<ll,ll,ll,ll>> points;
auto gcd_func = [](ll a, ll b) -> ll {
a = abs(a); b = abs(b);
while (b) { a %= b; swap(a, b); }
return a;
};
for (int i = 0; i < N; i++) {
ll x1 = segs[i].x1, y1 = segs[i].y1;
ll x2 = segs[i].x2, y2 = segs[i].y2;
ll dx1 = x2 - x1, dy1 = y2 - y1;
for (int j = i + 1; j < N; j++) {
ll x3 = segs[j].x1, y3 = segs[j].y1;
ll x4 = segs[j].x2, y4 = segs[j].y2;
ll dx2 = x4 - x3, dy2 = y4 - y3;
// D = dx1 * dy2 - dy1 * dx2
ll D = dx1 * dy2 - dy1 * dx2;
if (D == 0) continue; // parallel or collinear
// t_num = (x3 - x1) * dy2 - (y3 - y1) * dx2
// u_num = (x3 - x1) * dy1 - (y3 - y1) * dx1
ll t_num = (x3 - x1) * dy2 - (y3 - y1) * dx2;
ll u_num = (x3 - x1) * dy1 - (y3 - y1) * dx1;
// Interior: 0 < t < 1 and 0 < u < 1
// t = t_num / D, u = u_num / D
// If D > 0: 0 < t_num < D and 0 < u_num < D
// If D < 0: D < t_num < 0 and D < u_num < 0
bool t_ok, u_ok;
if (D > 0) {
t_ok = (t_num > 0 && t_num < D);
u_ok = (u_num > 0 && u_num < D);
} else {
t_ok = (t_num < 0 && t_num > D);
u_ok = (u_num < 0 && u_num > D);
}
if (!t_ok || !u_ok) continue;
// Compute intersection point: (x1 + t * dx1, y1 + t * dy1)
// = (x1 + t_num * dx1 / D, y1 + t_num * dy1 / D)
// = ((x1 * D + t_num * dx1) / D, (y1 * D + t_num * dy1) / D)
lll px = (lll)x1 * D + (lll)t_num * dx1;
lll py = (lll)y1 * D + (lll)t_num * dy1;
lll dd = D;
// Normalize: dd > 0
if (dd < 0) { px = -px; py = -py; dd = -dd; }
// Reduce to lowest terms
ll px_l = (ll)px, py_l = (ll)py, dd_l = (ll)dd;
ll g = gcd_func(gcd_func(abs(px_l), abs(py_l)), dd_l);
px_l /= g; py_l /= g; dd_l /= g;
points.insert({px_l, py_l, dd_l, 0});
}
}
cout << points.size() << endl;
return 0;
}
"""
Problem 165: Intersections
Count distinct interior intersection points of 5000 line segments
generated by the Blum Blum Shub pseudo-random number generator.
Uses exact rational arithmetic to identify distinct points.
"""
from math import gcd
def solve():
N = 5000
# Generate segments using BBS
s = 290797
coords = []
for _ in range(4 * N):
s = s * s % 50515093
coords.append(s % 500)
segments = []
for i in range(N):
x1, y1, x2, y2 = coords[4*i], coords[4*i+1], coords[4*i+2], coords[4*i+3]
segments.append((x1, y1, x2, y2))
points = set()
for i in range(N):
x1, y1, x2, y2 = segments[i]
dx1, dy1 = x2 - x1, y2 - y1
for j in range(i + 1, N):
x3, y3, x4, y4 = segments[j]
dx2, dy2 = x4 - x3, y4 - y3
D = dx1 * dy2 - dy1 * dx2
if D == 0:
continue
t_num = (x3 - x1) * dy2 - (y3 - y1) * dx2
u_num = (x3 - x1) * dy1 - (y3 - y1) * dx1
# Interior: 0 < t < 1 and 0 < u < 1
if D > 0:
if not (0 < t_num < D and 0 < u_num < D):
continue
else:
if not (D < t_num < 0 and D < u_num < 0):
continue
# Intersection: (x1 + t_num/D * dx1, y1 + t_num/D * dy1)
# = (x1*D + t_num*dx1, y1*D + t_num*dy1) / D
px = x1 * D + t_num * dx1
py = y1 * D + t_num * dy1
dd = D
if dd < 0:
px, py, dd = -px, -py, -dd
g = gcd(gcd(abs(px), abs(py)), dd)
px //= g
py //= g
dd //= g
points.add((px, py, dd))
print(len(points))
solve()