Collinearity
June 17, 2014
Beware: today’s exercise sounds simple but is actually quite complex if you don’t look at it properly.
Your task is to write a function that takes three points in the x,y plane and determines if they are collinear; be sure to handle vertical lines and horizontal lines properly. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.
In Python.
My simple solution uses the fact that point lying on the same line form a triangle of area zero.
This code just does that.
Using area of triangle is 0, includes duplicate points and avoids zero division.
(define re real-part) (define im imag-part) (define mag magnitude)
(define (dot u w) (+ (* (re u) (re w)) (* (im u) (im w))))
(define (collinearish? u v w)
(let ((uv (- u v)) (uw (- u w)) (eps 1e-14))
(or (< (mag uv) eps) (< (mag uw) eps)
(< (abs (- (cos 0) (abs (/ (dot uv uw) (mag uv) (mag uw))))) eps))))
A complex solution, pun intended, to within an epsilon: representing points as rectangular numbers in the obvious way, then computing the cosine of the angle between two vectors. (Or if at least one of the two vectors is null, the three points are at most two, and so collinear.)
If A, B and C are collinear, the vector product of AB and AC is zero.
bool collinear(x1, y1, x2, y2, x3, y3) {
return ((x2 – x1)*(y3 – y1) – (x3 – x1)*(y2 – y1) == 0);
}
I like pbo’s solution – it’s equivalent to taking AB and BC, rotating BC 90 degrees and taking the dot product. This gives |AB| *
We can extend to a nice floating point solution, for consistency (and to minimize rounding errors) we will take all three difference vectors and work with the two longest ones (this also deals nicely with have two of the points very close to each other). Having calculated the dot product, we then scale by the length of the longest vector to get a dimensionless indication of collinearity (so scaling up all the points by a constant factor doesn’t affect the result). For simplicity, we use the taxicab metric for computing vector lengths. (Needs C++11 for auto).
Here is a Racket Solution. I use the fact that three collinear points produce a triangle of area 0. My solution uses an epsilon value to take into account error propagation in floating point, so it is a probabilistic method, although very accurate.
I used the same approach as pbo:
Codepad
Attempt to embed:
module Collinear (collinear) where
(.-) :: Num a => (a, a) -> (a, a) -> (a, a)
(x1, x2) .- (y1, y2) = (x1 – y1, x2 – y2)
cross :: Num a => (a, a) -> (a, a) -> a
(x1, x2) `cross` (y1, y2) = x1 * y2 – x2 * y1
collinear :: (Num a, Eq a) => (a, a) -> (a, a) -> (a, a) -> Bool
collinear v1 v2 v3 = (v1 .- v2) `cross` (v1 .- v3) == 0
Javascript Code…
/*input*/
var points = [
{
x : 5,
y : 5
},
{
x : 15,
y : 15
},
{
x : 40,
y : 40
}
];
/*logic*/
function isCoLinear(points){
return (Math.atan( Math.abs((points[1][“y”] – points[0][“y”])/(points[1][“x”] – points[0][“x”])) ) * (180/Math.PI)) ===
(Math.atan( Math.abs((points[2][“y”] – points[0][“y”])/(points[2][“x”] – points[0][“x”])) ) * (180/Math.PI))
}
isCoLinear(points);