# python3 program to find maximum number of 2D points that lie on the same line.
from collections import defaultdict
from math import gcd
from typing import DefaultDict, List, Tuple
IntPair = Tuple[int, int]
def normalized_slope(a: IntPair, b: IntPair) -> IntPair:
"""
Returns normalized (rise, run) tuple. We won't return the actual rise/run
result in order to avoid floating point math, which leads to faulty
comparisons.
See
https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems
"""
run = b[0] - a[0]
# normalize undefined slopes to (1, 0)
if run == 0:
return (1, 0)
# normalize to left-to-right
if run < 0:
a, b = b, a
run = b[0] - a[0]
rise = b[1] - a[1]
# Normalize by greatest common divisor.
# math.gcd only works on positive numbers.
gcd_ = gcd(abs(rise), run)
return (
rise // gcd_,
run // gcd_,
)
def maximum_points_on_same_line(points: List[List[int]]) -> int:
# You need at least 3 points to potentially have non-collinear points.
# For [0, 2] points, all points are on the same line.
if len(points) < 3:
return len(points)
# Note that every line we find will have at least 2 points.
# There will be at least one line because len(points) >= 3.
# Therefore, it's safe to initialize to 0.
max_val = 0
for a_index in range(0, len(points) - 1):
# All lines in this iteration go through point a.
# Note that lines a-b and a-c cannot be parallel.
# Therefore, if lines a-b and a-c have the same slope, they're the same
# line.
a = tuple(points[a_index])
# Fresh lines already have a, so default=1
slope_counts: DefaultDict[IntPair, int] = defaultdict(lambda: 1)
for b_index in range(a_index + 1, len(points)):
b = tuple(points[b_index])
slope_counts[normalized_slope(a, b)] += 1
max_val = max(
max_val,
max(slope_counts.values()),
)
return max_val
print(maximum_points_on_same_line([
[-1, 1],
[0, 0],
[1, 1],
[2, 2],
[3, 3],
[3, 4],
]))
# This code is contributed by Jose Alvarado Torre