The problem in simple words: I have three arrays of double values (x-, y- and z-coordinates) and need to find the index of the point which has minimum distance to a reference points. I do not need the actual value of this distance. If the minimum distance is not unique, any index where its value occurs may be returned.
I will be using this function for determining the squared distance between points:
double distSqr(double xa, double ya, double za,
double xb, double yb, double zb)
{
double X = xa - xb;
double Y = xa - xb;
double Z = xa - xb;
return X * X + Y * Y + Z * Z;
}
Taking the square root in the final step will not be necessary.
First of all, consider a "naive" implementation where you simply compare the "current" value to all other values, exchanging the "current" value if a smaller value is found:
// Take the three arrays of x-, y-, and z-coordinates filled with arbitrary values.
#define SIZE 20
double cx[SIZE];
double cy[SIZE];
double cz[SIZE];
int min_naive(double x, double y, double z)
{
double minDist = distSqr(x, y, z, cx[0], cy[0], cz[0]);
int minIndex = 0;
for (int i = 1; i < SIZE; i++)
{
double d = distSqr(x, y, z, cx[i], cy[i], cz[i]);
if (d < minDist)
{
minDist = d;
minIndex = i;
}
}
return minIndex;
}
// Finally, determin the index, e.g.
int minInd = min_naive(0, 0, 0);
This does not seem like an optimal solution to me, especially since it might not be fast enough if the loop has to run over all distances where only one comparison happens at each step. Obviously, sooner or later all distances have to be calculated. Next, I came up with a better solution that first calculates all distances and then compares them in a loop in such a way, that each loop halves the number of possible distances left. I hoped that the compiler might auto-vectorize this function, instead of using a simple loop over all points, but I have not checked that yet.
This is the code I came up with:
void cmpmv(int lower, int upper, int *inds, double *dists)
{
double a = dists[lower];
double b = dists[upper];
if (a > b)
{
inds[lower] = inds[upper];
dists[lower] = b;
}
}
int min_better(double kax, double kay, double kaz)
{
double dists[SIZE];
int inds[SIZE];
for (int i = 0; i < SIZE; i++)
{
dists[i] = distSqr(kax, kay, kaz, nncx[i], nncy[i], nncz[i]);
inds[i] = i;
}
int div, mod;
int s = SIZE;
while (s > 1)
{
div = s / 2;
mod = s % 2;
for (int i = 0; i < div; i++)
cmpmv(i, i + div, inds, dists);
if (mod == 1)
cmpmv(0, s - 1, inds, dists);
s = div;
}
return inds[0];
}
(Note that this must differentiate the cases where SIZE
and later s
are not a multiple of 2.)
Are there maybe even better and especially faster ways to implement this?
There are two restrictions:
- I only have one thread available.
- I have to use arrays/pointers.