December 2005 Problem 'align' Analysis

by Rob Kolstad

The problem is "find sets of three collinear points from an input set" and furthermore to print them in a certain order. Regrettably, one must count the solutions before outputting them. Thus, either one must traverse the data twice or one must save the solutions and then output them (presumably, this is twice as fast as doing the problem twice).

Collinearity is probably most easily determined by determining whether the slope from some point 1 to some point 2 is the same as the slope from that same point 1 to some other point 3. One might be tempted to write code that compares slopes like this:

    double slope1 = (y[j]-y[i])/(x[j]-x[i]);
    double slope2 = (y[k]-y[i])/(x[k]-x[i]);
    if (slope1 == slope2) {
        /* solution found */
    }
but this has the dual downsides of using slower floating point arithmetic (slower than integers, anyway) and -- most dangerously -- performing a floating point compare. It is axiomatic that floating point compares suffer from failure due to slight inaccuracies of floating point representation. Thus, one needs to cross-multiply:
    if ( (y[j]-y[i]) * (x[k]-x[i]) == (y[k]-y[i]) * (x[j]-x[i]) ) {
	/* solution found */
    }
This calculation takes place entirely in integer arithmetic, with the only downside of potential overflow if the values work out poorly.

What's left? The triple nested loop so see the solutions.

Here is Rob's solution, written to work the first time with as much speed as one can muster for a first-try effort (which explains all the temporary variable assignments):

#include <stdio.h>
#include <math.h>

int coors[2000][2];	/* x,y */
int ans[2000][3];	/* cow ids */
int ncollinear;

idcompare(i,j) {
    int k;
    for (k = 0; k < 3; k++) {
	if (ans[i][k] < ans[j][k]) return -1;
	if (ans[i][k] > ans[j][k]) return 1;
    }
    return 0;
}

main () {
    int i, j, k, n, t;
    int x1,y1,  x2,y2, x3,y3, dx1,dy1, dx2,dy2;
    FILE *fin  = fopen ("align.in", "r");
    FILE *fout = fopen ("align.out", "w");
    fscanf(fin, "%d", &n);
    for (i = 0; i < n; i++)
	fscanf(fin, "%d %d", &coors[i][0], &coors[i][1]);
    for (i = 0; i < n; i++) {
	x1 = coors[i][0];
	y1 = coors[i][1];
	for (j = i+1; j < n; j++) {
	    x2 = coors[j][0];
	    y2 = coors[j][1];
	    dx1 = x2 - x1;
	    dy1 = y2 - y1;
	    for (k = j+1; k < n; k++) {
	        x3 = coors[k][0];
	        y3 = coors[k][1];
		dx2 = x3 - x2;
		dy2 = y3 - y2;
		if (dy1 * dx2 == dy2 * dx1) {
		    ans[ncollinear][0]  = i+1;	/* i,j,k already sorted */
		    ans[ncollinear][1]  = j+1;
		    ans[ncollinear][2]  = k+1;
		    ncollinear++;
		}
	    }
	}
    }
    fprintf(fout, "%d\n", ncollinear);
    for (i = 0; i < ncollinear; i++) 
	fprintf (fout, "%d %d %d\n", ans[i][0], ans[i][1], ans[i][2]);
    exit (0);
}