Project Euler Problem 972
nThe hyperbolic plane can be represented by the open unit disc, namely the set of points (x, y) in Bbb R^2 with x^2 + y^
Solution
Answer: 3575508
Let
$$s(P)=x^2+y^2+1.$$
A hyperbolic geodesic in the Poincaré disc is either:
- a diameter through the origin, or
- a circle orthogonal to the unit circle.
Such circles have equation
$$x^2+y^2+Ax+By+1=0.$$
Therefore three points $P_i=(x_i,y_i)$ lie on a common geodesic iff there exist $A,B$ such that
$$x_i^2+y_i^2+A x_i+B y_i+1=0$$
for all $i$. Eliminating $A,B$ gives the determinant condition
$$\det \begin{pmatrix} x_1 & y_1 & x_1^2+y_1^2+1\ x_2 & y_2 & x_2^2+y_2^2+1\ x_3 & y_3 & x_3^2+y_3^2+1 \end{pmatrix}=0.$$
So every point corresponds to a projective point
$$(x,y,x^2+y^2+1),$$
and geodesics correspond exactly to projective lines.
The computation strategy is:
- Generate all rational points in the unit disc with denominator $\le 12$.
- Convert each point to an integer projective vector.
- Every pair determines a unique projective line (via cross product).
- Count how many points lie on each line.
- Sum $k(k-1)(k-2)$ over all lines, because ordered triples are required.
The following C++ program performs the exact computation:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct Key {
ll a,b,c;
};
struct H {
size_t operator()(Key const& k) const noexcept {
return ((uint64_t)k.a * 1315423911u)
^ ((uint64_t)k.b << 21)
^ k.c;
}
};
struct E {
bool operator()(Key const& x, Key const& y) const noexcept {
return x.a==y.a && x.b==y.b && x.c==y.c;
}
};
ll gcd3(ll a,ll b,ll c){
return gcd(gcd(llabs(a), llabs(b)), llabs(c));
}
int main() {
int N = 12;
vector<pair<int,int>> vals;
set<pair<int,int>> seen;
// all reduced rationals with denominator <= N
for(int q=1;q<=N;q++){
for(int p=-q+1;p<q;p++){
int g = gcd(abs(p), q);
pair<int,int> t = {p/g, q/g};
if(!seen.count(t)){
seen.insert(t);
vals.push_back(t);
}
}
}
// projective points
vector<array<int,3>> pts;
for(auto [p,q] : vals){
for(auto [r,s] : vals){
if(1LL*p*p*s*s + 1LL*r*r*q*q < 1LL*q*q*s*s){
int g = gcd(q,s);
int l = q/g*s;
ll X = 1LL*p*(l/q);
ll Y = 1LL*r*(l/s);
ll a = X*l;
ll b = Y*l;
ll c = X*X + Y*Y + 1LL*l*l;
ll gg = gcd3(a,b,c);
pts.push_back({
(int)(a/gg),
(int)(b/gg),
(int)(c/gg)
});
}
}
}
unordered_map<Key,int,H,E> mp;
mp.reserve(30000000);
int n = pts.size();
// each pair determines a line
for(int i=0;i<n;i++){
auto [a1,b1,c1] = pts[i];
for(int j=i+1;j<n;j++){
auto [a2,b2,c2] = pts[j];
ll A = 1LL*b1*c2 - 1LL*c1*b2;
ll B = 1LL*c1*a2 - 1LL*a1*c2;
ll C = 1LL*a1*b2 - 1LL*b1*a2;
ll g = gcd3(A,B,C);
A/=g; B/=g; C/=g;
if(A<0 || (A==0 && (B<0 || (B==0 && C<0)))){
A=-A; B=-B; C=-C;
}
mp[{A,B,C}]++;
}
}
long long ans = 0;
// if a line has k points, it contributes k(k-1)(k-2)
for(auto &kv : mp){
long long v = kv.second;
// v = C(k,2)
long long s = sqrtl(1 + 8*v);
long long k = (1 + s)/2;
ans += k*(k-1)*(k-2);
}
cout << ans << endl;
}
Verification on the examples:
- $T(2)=24$
- $T(3)=1296$
both match the statement.
Running the computation for $N=12$ gives:
Answer: 3575508