implement divide and conquer

This commit is contained in:
Chris Cromer 2018-12-12 18:48:54 -03:00
parent e37384d18f
commit 6ac113d408
Signed by: cromer
GPG Key ID: 39CC813FF3C8708A
4 changed files with 159 additions and 73 deletions

View File

@ -23,5 +23,5 @@
* @return Retorna la distancia entre los 2 puntos * @return Retorna la distancia entre los 2 puntos
*/ */
double distance(point_t point1, point_t point2) { 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);
} }

View File

@ -14,74 +14,167 @@
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include "points.h" #include "points.h"
#include "brute_force.h" #include "brute_force.h"
#include "distance.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 * 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 n La cantidad de puntos en el array point_ts
* @param minimum_dist La distancia minimo encontrado * @param minimum_dist La distancia minimo encontrado
* @return Retorna los 2 puntos mas cercanos * @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 * divide_and_conquer(point_t *points, unsigned int n, double *minimum_dist) {
point_t *closest_pair = malloc(sizeof(point_t) * 2); point_t *closest_pair = malloc(sizeof(point_t) * 2);
qsort(points, n, sizeof(point_t), compareX); point_t *points_x = malloc(sizeof(point_t) * n);
closestUtil(points, n, minimum_dist, closest_pair); //Usa la funcion recursiva closestUtil para encontrar la distancia minima 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; return closest_pair;
} }

View File

@ -18,6 +18,7 @@
#include <getopt.h> #include <getopt.h>
#include <string.h> #include <string.h>
#include <float.h> #include <float.h>
#include <math.h>
#include "points.h" #include "points.h"
#include "distance.h" #include "distance.h"
#include "timer.h" #include "timer.h"
@ -166,26 +167,18 @@ int main (int argc, char **argv) {
end_algorithm(); end_algorithm();
fprintf(stdout, "point 1: x: %f y: %f\n", closest_points[0].x, closest_points[0].y); 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, "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); free(closest_points);
closest_points = NULL; closest_points = NULL;
} }
if (divide) { 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); start_algorithm("Divide and conquer corriendo... ", n);
closest_points = divide_and_conquer(points, n, &dist); closest_points = divide_and_conquer(points, n, &dist);
end_algorithm(); end_algorithm();
}
fprintf(stdout, "point 1: x: %f y: %f\n", closest_points[0].x, closest_points[0].y); 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, "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); free(closest_points);
closest_points = NULL; closest_points = NULL;
} }

View File

@ -30,7 +30,7 @@
* @param d2 El segudno double * @param d2 El segudno double
* @return Retorna 1 si son igual o 0 sino * @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; double precision = 0.000000001;
if (((d1 - precision) < d2) && ((d1 + precision) > d2)) { if (((d1 - precision) < d2) && ((d1 + precision) > d2)) {
return 1; return 1;
@ -72,7 +72,7 @@ int main(int argc, char **argv) {
fprintf(stdout, "\tbrute force: "); fprintf(stdout, "\tbrute force: ");
fflush(stdout); fflush(stdout);
brute_force(points, n, &dist); brute_force(points, n, &dist);
if (compare_double(dist, 0.067687840)) { if (compare_double(sqrt(dist), 0.067687840)) {
fprintf(stdout, "passed\n"); fprintf(stdout, "passed\n");
passed++; passed++;
} }
@ -90,7 +90,7 @@ int main(int argc, char **argv) {
fprintf(stdout, "\tdivide and conquer: "); fprintf(stdout, "\tdivide and conquer: ");
fflush(stdout); fflush(stdout);
divide_and_conquer(points, n, &dist); divide_and_conquer(points, n, &dist);
if (compare_double(dist, 0.067687840)) { if (compare_double(sqrt(dist), 0.067687840)) {
fprintf(stdout, "passed\n"); fprintf(stdout, "passed\n");
passed++; passed++;
} }