diff --git a/src/distance.c b/src/distance.c index 9fb0c6c..16b9871 100644 --- a/src/distance.c +++ b/src/distance.c @@ -23,5 +23,5 @@ * @return Retorna la distancia entre los 2 puntos */ double distance(point_t point1, point_t point2) { - return sqrt(pow(point1.x - point2.x, 2) + pow(point1.y - point2.y, 2)); + return pow(point1.x - point2.x, 2) + pow(point1.y - point2.y, 2); } diff --git a/src/divide_and_conquer.c b/src/divide_and_conquer.c index 1f29a97..390edbe 100644 --- a/src/divide_and_conquer.c +++ b/src/divide_and_conquer.c @@ -14,74 +14,167 @@ */ #include +#include +#include +#include #include "points.h" #include "brute_force.h" #include "distance.h" +/** + * Comparar 2 double para ver cual es mayor o si son iguales + * @param a El primer double a comparar + * @param b El segundo double a comparar + * @return Retorna 1 si a es mayor de b, -1 si a es menor de b ó 0 si son igual + */ +int compare_double(double a, double b) { + if (a > b) { + return 1; + } + else if (a < b) { + return -1; + } + else { + return 0; + } +} + +/** + * Comparar los el eje x de 2 puntos + * @param a El primer punto a comparar + * @param b El segundo punto a comparar + * @return Retorna la comparación entre los 2 puntos + */ +int compare_x(const void * a, const void * b) { + return compare_double((*(point_t *) a).x, (*(point_t *) b).x); +} + +/** + * Comparar los el eje y de 2 puntos + * @param a El primer punto a comparar + * @param b El segundo punto a comparar + * @return Retorna la comparación entre los 2 puntos + */ +int compare_y(const void * a, const void * b) { + return compare_double((*(point_t *) a).y, (*(point_t *) b).y); +} + +/** + * El algoritmo de encontrar 2 puntos recursivo usando divide and conquer + * @param points_x Los puntos ordenado en el eje x + * @param nx La cantidad de puntos en el array points_y + * @param points_y Los puntos ordenado en el eje y + * @param ny La cantidad de puntos en el array points_y + * @param closest_pair Las 2 pares de puntos mas cercano + * @return Retorna la distance entre los dos puntos mas cercano + */ +double divide_and_conquer_run(point_t *points_x, unsigned int nx, point_t *points_y, unsigned int ny, point_t *closest_pair) { + int left; + int right; + int i; + double mid; + double d = DBL_MAX; + double min_d = DBL_MAX; + double x0; + double x1; + double x; + point_t *closest_pair2; + point_t *points_y2; + if (nx <= 4) { + closest_pair2 = brute_force(points_x, nx, &d); + closest_pair[0] = closest_pair2[0]; + closest_pair[1] = closest_pair2[1]; + return d; + } + + closest_pair2 = malloc(sizeof(point_t) * 2); + points_y2 = malloc(sizeof(point_t) * ny); + mid = points_x[nx / 2].x; + + left = -1; + right = ny; + for (i = 0; i < ny; i++) { + if (points_y[i].x < mid) { + points_y2[++left] = points_y[i]; + } + else { + points_y2[--right]= points_y[i]; + } + } + + for (i = ny - 1; right < i; right ++, i--) { + closest_pair2[0] = points_y2[right]; + points_y2[right] = points_y2[i]; + points_y2[i] = closest_pair2[0]; + } + + min_d = divide_and_conquer_run(points_x, nx / 2, points_y2, left + 1, closest_pair); + d = divide_and_conquer_run(points_x + nx / 2, nx - nx / 2, points_y2 + left + 1, ny - left - 1, closest_pair2); + + if (d < min_d) { + min_d = d; + closest_pair[0] = closest_pair2[0]; + closest_pair[1] = closest_pair2[1]; + } + d = sqrt(min_d); + free(closest_pair2); + + left = -1; right = ny; + for (i = 0; i < ny; i++) { + x = points_y[i].x - mid; + if (x <= -d || x >= d) { + continue; + } + if (x < 0) { + points_y2[++left] = points_y[i]; + } + else { + points_y2[--right] = points_y[i]; + } + } + + while (left >= 0) { + x0 = points_y2[left].y + d; + + while (right < ny && points_y2[right].y > x0) { + right ++; + } + if (right >= ny) { + break; + } + + x1 = points_y2[left].y - d; + for (i = right; i < ny && points_y2[i].y > x1; i++) + if ((x = distance(points_y2[left], points_y2[i])) < min_d) { + min_d = x; + closest_pair[0] = points_y2[left]; + closest_pair[1] = points_y2[i]; + } + left--; + } + + free(points_y2); + return min_d; +} + /** * Encontrar los 2 puntos más cercano usando el metodo de dividir para conquistar - * @param point_ts Los puntos a calcular + * @param point_t Los puntos a calcular * @param n La cantidad de puntos en el array point_ts * @param minimum_dist La distancia minimo encontrado * @return Retorna los 2 puntos mas cercanos */ -int compareX(const void* a, const void* b){ //ordena el arreglo de puntos de acuerdo a X - point_t *p1 = (point_t *)a, *p2 = (point_t *)b; - return (p1->x - p2->x); -} - -int compareY(const void* a, const void* b){ //ordena el arreglo de puntos de acuerdo a Y - point_t *p1 = (point_t *)a, *p2 = (point_t *)b; - return (p1->y - p2->y); -} - -double min(double x, double y){ // Función para encontrar el mayor entre dos valores flotantes - return (x < y)? x : y; -} - -void stripClosest(point_t *strip, int n, double *minimum_dist, point_t *closest_pair) // Función para encontrar la distancia entre los puntos más cerca del arreglo dado -{ - double dist; - qsort(strip, n, sizeof(point_t), compareY); - - for (int i = 0; i < n; ++i) - for (int j = i+1; j < n && (strip[j].y - strip[i].y) < *minimum_dist; ++j) - if ((dist = distance(strip[i],strip[j])) < *minimum_dist){ - *minimum_dist = dist; - closest_pair[0] = strip[i]; - closest_pair[1] = strip[j]; - } -} - -double closestUtil(point_t *P, int n, double *minimum_dist, point_t *closest_pair){ // Función para encontrar la distancia más corta entre puntos - int mid = n/2; - point_t midpoint_t = P[mid]; - - // Consider the vertical line passing through the middle point_t - // calculate the smallest distance dl on left of middle point_t and - // dr on right side - double dl = closestUtil(P, mid, minimum_dist, closest_pair); - double dr = closestUtil(P + mid, n-mid, minimum_dist, closest_pair); - - double d = min(dl, dr); // Encontrar la distancia minima de dos puntos - - /* Crea un arreglo que contiene los puntos cercanos, mas cerca que d*/ - point_t strip[n]; - int j = 0; - for (int i = 0; i < n; i++) - if (abs(P[i].x - midpoint_t.x) < d) - strip[j] = P[i], j++; - - /*Encontrar el punto más cercano en la cinta, retornando el minimo de d y el más cercano - distance es strip[]*/ - *minimum_dist = min(d, stripClosest(strip, j, minimum_dist, closest_pair) ); - return *minimum_dist; -} - point_t * divide_and_conquer(point_t *points, unsigned int n, double *minimum_dist) { point_t *closest_pair = malloc(sizeof(point_t) * 2); - qsort(points, n, sizeof(point_t), compareX); - closestUtil(points, n, minimum_dist, closest_pair); //Usa la funcion recursiva closestUtil para encontrar la distancia minima + point_t *points_x = malloc(sizeof(point_t) * n); + point_t *points_y = malloc(sizeof(point_t) * n); + memcpy(points_x, points, sizeof(point_t) * n); + memcpy(points_y, points, sizeof(point_t) * n); + qsort(points_x, n, sizeof(point_t), compare_x); + qsort(points_y, n, sizeof(point_t), compare_y); + *minimum_dist = divide_and_conquer_run(points_x, n, points_y, n, closest_pair); + free(points_x); + free(points_y); return closest_pair; } diff --git a/src/points.c b/src/points.c index dc9d897..3b79c85 100644 --- a/src/points.c +++ b/src/points.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "points.h" #include "distance.h" #include "timer.h" @@ -166,26 +167,18 @@ int main (int argc, char **argv) { end_algorithm(); fprintf(stdout, "point 1: x: %f y: %f\n", closest_points[0].x, closest_points[0].y); fprintf(stdout, "point 2: x: %f y: %f\n", closest_points[1].x, closest_points[1].y); - fprintf(stdout, "distance: %lf\n\n", dist); + fprintf(stdout, "distance: %lf\n\n", sqrt(dist)); free(closest_points); closest_points = NULL; } if (divide) { - if (n <= 3) { - fprintf(stderr, "Divide and conquer necesita 4 o mas puntos!\nSe utilizará Brute Force como alternativa!\n"); - start_algorithm("Brute force corriendo... ", n); - closest_points = brute_force(points, n, &dist); - end_algorithm(); - } - else { - start_algorithm("Divide and conquer corriendo... ", n); - closest_points = divide_and_conquer(points, n, &dist); - end_algorithm(); - } + start_algorithm("Divide and conquer corriendo... ", n); + closest_points = divide_and_conquer(points, n, &dist); + end_algorithm(); fprintf(stdout, "point 1: x: %f y: %f\n", closest_points[0].x, closest_points[0].y); fprintf(stdout, "point 2: x: %f y: %f\n", closest_points[1].x, closest_points[1].y); - fprintf(stdout, "distance: %lf\n", dist); + fprintf(stdout, "distance: %lf\n", sqrt(dist)); free(closest_points); closest_points = NULL; } diff --git a/test/test.c b/test/test.c index 8679b38..ff96157 100644 --- a/test/test.c +++ b/test/test.c @@ -30,7 +30,7 @@ * @param d2 El segudno double * @return Retorna 1 si son igual o 0 sino */ -int compare_double(double d1, double d2) { +inline int compare_double(double d1, double d2) { double precision = 0.000000001; if (((d1 - precision) < d2) && ((d1 + precision) > d2)) { return 1; @@ -72,7 +72,7 @@ int main(int argc, char **argv) { fprintf(stdout, "\tbrute force: "); fflush(stdout); brute_force(points, n, &dist); - if (compare_double(dist, 0.067687840)) { + if (compare_double(sqrt(dist), 0.067687840)) { fprintf(stdout, "passed\n"); passed++; } @@ -90,7 +90,7 @@ int main(int argc, char **argv) { fprintf(stdout, "\tdivide and conquer: "); fflush(stdout); divide_and_conquer(points, n, &dist); - if (compare_double(dist, 0.067687840)) { + if (compare_double(sqrt(dist), 0.067687840)) { fprintf(stdout, "passed\n"); passed++; }