Академический Документы
Профессиональный Документы
Культура Документы
Tabla de contenidos
Intro ...................................................................................................................... 1 Autora y Copyright ...................................................................................... 2 Revisiones .................................................................................................... 2 Nota .............................................................................................................. 2 Planteamiento del Caso ...................................................................................... 2 Preparacin de datos de Prueba ................................................................. 3 Variantes del Algoritmo ................................................................................ 3 Implementacin de Objetos ......................................................................... 4 Eclipse .......................................................................................................... 4 Implementacin en Lenguaje C++ ....................................................................... 4 Rutina principal main .................................................................................. 4 Archivos Cabecera ....................................................................................... 5 Implementaciones ........................................................................................ 7 Implementacin en Lenguaje C ......................................................................... 12 Rutina principal main ................................................................................ 12 Archivos Cabecera ..................................................................................... 13 Implementaciones ...................................................................................... 14 Implementacin en Lenguaje Java .................................................................... 18 Rutina principal main ................................................................................ 19 Implementacin de Clases ......................................................................... 20 Resultados ......................................................................................................... 24 Observaciones ........................................................................................... 25 A. Descripcin del Algoritmo de Decisin ........................................................... 26 Primera Variante ........................................................................................ 26 Punto al interior del un tringulo ........................................................ 27 Puntos con respecto a una recta ....................................................... 27 Segunda Variante ...................................................................................... 28 Tercera Variante ........................................................................................ 29 B. Referencias ..................................................................................................... 30
Intro
Este documento ilustra la implementacin de un algoritmo geomtrico sencillo (potencialmente utilizable en la programacin de juegos) en los lenguajes C, C+ + y Java, efectuando una comparacin de la performance relativa. La audiencia potencial son los desarrolladores que utilizan cualquiera de estos lenguajes y desean tener una idea acerca de la performance esperada en las implementaciones que realizan una gran cantidad de clculos aritmticos. 1
Autora y Copyright
Este documento tiene copyright (c) 2006 AmericaTI EIRL (www.americati.com.) Se otorga permiso para copiar, distribuir y/o modificar este documento bajo los trminos de la "GNU Free Documentation License, Version 1.2", excepto en lo mencionado en el siguiente prrafo. Esta licencia puede obtenerse en: http://www.gnu.org/licenses/ fdl.txt Si se desea crear un trabajo derivado o publicar este documento para cualquier propsito, por favor contactarnos (va nuestra pgina web) a fin de tener la oportunidad de proporcionar una versin ms reciente. De no ser esto posible, la ltima versin debera estar disponible en el sitio web AmericaTI.com. [http:// www.americati.com] Son bienvenidas todas las sugerencias y correcciones. Este documento fue confeccionado con DocBook utilizando el preprocesador Qdk disponible en SourceForge.net. [http://qdk.sourceforge.net]
Revisiones
0.1 2006-04-06 Primera versin completa
Nota
Este documento puede ser ledo en compaa del artculo "Ventajas y Desventajas entre C, C++ y Java", disponible en AmericaTI.com, el cual proporciona diversos aspectos tericos importantes. El presente material va directamente a la implementacin de un caso prctico.
Caso Comparativo con C, C++ y Java A fin de simplificar la exposicin, asumiremos tambin que los polgonos son convexos (no tienen cavidades o entradas o agujeros.) El apndice "A" proporciona una explicacin intuitiva pero detallada del algoritmo que ser implementado a continuacin.
long semilla=170374; long aleatorio(void) { long k,ans; semilla^=MASK; k=semilla/IQ; semilla=IA*(semilla-k*IQ)-IR*k; if(semilla<0) semilla+=ALEATORIO_MAX; ans=semilla; semilla^=MASK; return ans; }
Ntese que su "traduccin" a C++ y Java es trivial. Esta funcin ser utilizada para "preparar" los puntos de prueba y los polgnos de prueba para el anlisis principal. Ntese que no consideraremos la performance de esta etapa de preparacin.
Caso Comparativo con C, C++ y Java cuadrantes con respecto al punto de prueba. Esto consigue acelerar el proceso en un porcentaje considerable. La "variante 3" intenta evitar completamente los anteriores anlisis para el caso (tpicamente frecuente) en que el punto est bastante alejado del polgono. Con estas variantes obtendremos tres resultados distintos para cada una de los lenguajes de programacin. El cdigo que se presenta ms adelante implementa la "variante 3"; a partir de sta, deber ser muy sencillo para el lector comentar el cdigo necesario a fin de obtener las otras dos variantes.
Implementacin de Objetos
Los Puntos y los Polgonos son candidatos naturales para la creacin de clases. Para el caso del lenguaje C, crearemos dos estructuras y un conjunto de funciones que hacen un trabajo similar al que haran los mtodos correspondientes. Crearemos tambin una clase auxiliar utilitaria (llamada Util en C++ y AGUtil en Java) para agregar las rutinas de nmeros pseudoaleatorios y de clculo de tiempos. Para el C simplemente crearemos un archivo adicional. Los polgonos de prueba sern heptgonos regulares, lo cual considero que puede 1 ser un buen punto de partida para figuras ms complejas . Las implementaciones sern presentadas en el siguiente orden: 1) C++, 2) C ,y 3) Java. El motivo radica en que C++ puede considerarse un "punto medio" a partir de lo cual es fcil comprender las otras dos implementaciones.
Eclipse
Las tres implementaciones han sido elaboradas con ayuda del IDE Eclipse 3.2. Como es sabido, hasta esta versin Eclipse no proporciona automticamente soporte de C o C++, por lo que le agregamos el CDT (Eclipse C/C++ Development 2 Tools .)
1 2
Las pruebas equivalentes con tringulos arrojan resultados bastante distintos. El lector est invitado a probarlo. Para ms informacin acerca del CDT, acceder a: http://www-128.ibm.com/developerworks/opensource/library/osecc/ y http://www.eclipse.org/cdt/
Archivos Cabecera
5
Caso Comparativo con C, C++ y Java Tngase en cuenta que para los puntos y los polgonos se estn proporcionando ms operaciones de las estrictamente necesarias, las cuales pueden ser comentadas si se desea.
La clase utilitaria
La clase utilitaria implementa las funciones como mtodos estticos.
#ifndef UTIL_H_ #define UTIL_H_ class Util { private: static static public: static static };
long aleatorio(); long semilla; long aleatorio_entero(long a,long b); double getTime();
#endif /*UTIL_H_*/
Implementaciones
Implementacin del Punto
El nico mtodo complicado corresponde a insideTriangle(). Ver el apndice A para ms informacin.
#include "Point.h" #include <stdio.h> #include <math.h> using namespace std; Point::Point() { x=y=0; } Point::Point(double a,double b) { x=a; y=b; } Point::Point(const Point &a,const Point &b) { x=b.getX()-a.getX(); y=b.getY()-a.getY(); }
Point::Point(const Point &a) { x=a.getX(); y=a.getY(); } Point::~Point() { } double Point::getX() const { return x; } double Point::getY() const { return y; } void Point::setX(double n) { x=n; } void Point::setY(double n) { y=n; } void Point::print() const { printf("[%g,%g]",x,y); } double Point::distance2(const Point &p) const { double dx=x-p.getX(); double dy=y-p.getY(); return dx*dx+dy*dy; } double Point::distance(const Point &p) const { return sqrt(distance2(p)); } int Point::quadrant(const Point &p) const { if(p.getX()>=x) { if(p.getY()>y) return 1; else return 4; } else { if(p.getY()>y) return 2; else return 3; } } Point &Point::ort() { double t=y; y=x; x=-t;
10
Las variantes se pueden obtener fcilmente comentando el cdigo que invoca al mtodo distance2() (variante 2) y todo el cdigo relacionado a los cuadrantes "Q" (variante 1.)
long Util::semilla=170374; long Util::aleatorio(void) { long k,ans; semilla^=MASK; k=semilla/IQ; semilla=IA*(semilla-k*IQ)-IR*k; if(semilla<0) semilla+=ALEATORIO_MAX; ans=semilla; semilla^=MASK; return ans; } long Util::aleatorio_entero(long minimo,long maximo) { return minimo+(int)((maximo-minimo+1.0)*aleatorio()/(ALEATORIO_MAX+1.0)); } double Util::getTime() { struct timeval tv; struct timezone tz;
11
Implementacin en Lenguaje C
Rutina principal main
Anlogamente al caso C++, se proporcionan las implementaciones de tringulos y heptgonos. Notar que los mtodos de la clase "Point" han sido reemplazados con funciones con prefijo "Point_" a fin de mantener la similitud con el caso C++.
#include #include #include #include #include <stdio.h> <math.h> "Point.h" "Polygon.h" "Util.h"
static void setTriangle(Polygon *t); static void setHepta(Polygon *t); int main() { int z,j,interior=0; const int maxpol=10000,maxpnt=1000; Polygon pol[maxpol]; for(z=0;z<maxpol;z++) setHepta(&pol[z]); // setTriangle(pol[z]); Point p[maxpnt]; for(z=0;z<maxpnt;z++) { p[z].x=aleatorio_entero(0,800); p[z].y=aleatorio_entero(0,600); } double t1,t2; t1=getTime(); for(j=0;j<maxpnt;j++) for(z=0;z<maxpol;z++) { if(Polygon_isInterior(&pol[z],&p[j])) interior++; } t2=getTime(); double ratio=(interior*100.0)/(maxpol*maxpnt); printf("%d interiores de %d = %g %% (t=%g)\n", interior,maxpol*maxpnt,ratio,t2-t1); return 0; } static void setTriangle(Polygon *t) { Point p1;Point_set_double(&p1,aleatorio_entero(0,800), aleatorio_entero(0,600)); Point p2;Point_set_double(&p2,aleatorio_entero(0,800), aleatorio_entero(0,600)); Point p3;Point_set_double(&p3,aleatorio_entero(0,800), aleatorio_entero(0,600));
12
Archivos Cabecera
Declaracin del Punto
Esto es muy similar al caso C++, con la diferencia que se han extrado los mtodos como funciones globales:
#ifndef POINT_H_ #define POINT_H_ typedef struct { double x; double y; } Point; void Point_set_zero(Point *p); void Point_set_double(Point *p,double a,double b); void Point_set_2_point(Point *p, const Point *a,const Point *b); void Point_set_point(Point *p,const Point *a); void Point_print(const Point *p); double Point_distance2(const Point *p1,const Point *p2); double Point_distance(const Point *p1,const Point *p2); int Point_quadrant(const Point *me,const Point *p); void Point_ort(Point *p); void Point_add(Point *me,const Point *p); void Point_sub(Point *me,const Point *p); double Point_prod(const Point *p1,const Point *p2); int Point_insideTriangle(const Point *p, const Point *A, const Point *B, const Point *C); #endif /*POINT_H_*/
Caso Comparativo con C, C++ y Java La estructura Polygon tiene un detalle importante: almacena un puntero a estructuras Point. En tanto no existen rutinas de vectores en C, asignaremos la memoria dinmicamente (evidentemente, se pudo utilizar una librera auxiliar como "GLIB", pero esto nos alejara mucho del tema.)
#ifndef POLYGON_H_ #define POLYGON_H_ #include "Point.h" typedef struct { double diameter; Point *pt; int count; } Polygon; void Polygon_set_zero(Polygon *p); int Polygon_getCount(Polygon *p); const Point *Polygon_getPoint(const Polygon *p,int n); void Polygon_findDiameter(Polygon *p); void Polygon_setPoint(Polygon *me,int n,const Point *p); void Polygon_addPoint(Polygon *me,const Point *p); void Polygon_print(Polygon *p); int Polygon_isInterior(const Polygon *me,const Point *p); #endif /*POLYGON_H_*/
Implementaciones
Rutinas del Punto
Salvo la apariencia poco esttica, esto es casi una reescritura de la versin C++:
#include "Point.h" #include <stdio.h> #include <math.h> void Point_set_zero(Point *p) { p->x=p->y=0; }
14
15
void Polygon_set_zero(Polygon *p) { p->pt=NULL; p->diameter=0; p->count=0; } int Polygon_getCount(Polygon *p) { return p->count;
16
int Polygon_isInterior(const Polygon *me,const Point *p) { if(me->count<3) abort(); if(Point_distance2(p,Polygon_getPoint(me,0)) > me->diameter*me->diameter) return 0; int z,current,Q=0; for(z=0;z< me->count;z++) {
17
Rutinas Utilitarias
La implementacin de las funciones utilitarias es idntica al caso C++.
#include "Util.h" #include <sys/time.h> #include <time.h> #define #define #define #define #define #define IA 16807 IQ 127773 IR 2836 AM (1.0/ALEATORIO_MAX) MASK 123459876 ALEATORIO_MAX 2147483647L
static long semilla=170374; static long aleatorio(void) { long k,ans; semilla^=MASK; k=semilla/IQ; semilla=IA*(semilla-k*IQ)-IR*k; if(semilla<0) semilla+=ALEATORIO_MAX; ans=semilla; semilla^=MASK; return ans; } long aleatorio_entero(long minimo,long maximo) { return minimo+(int)((maximo-minimo+1.0)*aleatorio()/(ALEATORIO_MAX+1.0)); } double getTime() { struct timeval tv; struct timezone tz; gettimeofday(&tv,&tz); return tv.tv_sec+tv.tv_usec/1000000.0; }
//
19
Implementacin de Clases
Clase Point
La versin Java de Point es casi idntica al caso C++:
package com.americati.analgeom; public class Point { private double x; private double y; public Point() { x=y=0; } public Point(double a,double b) { x=a; y=b; } public Point(Point a,Point b) { x=b.getX()-a.getX(); y=b.getY()-a.getY(); } public Point(Point a) { x=a.getX(); y=a.getY(); }
public double getX() { return x; } public double getY() { return y; } public void setX(double n) { x=n;
20
21
Clase Polygon
El polgono contiene un vector de puntos. Ntese que usamos la sintaxis con "generics" de Java 5 (Vector<Point>.)
package com.americati.analgeom; import java.util.Vector; public class Polygon { private Vector<Point> pt; private double diameter; public Polygon() { diameter=0; pt=new Vector<Point>(); } public int getCount() { return pt.size(); } public Point getPoint(int n) { if(n<0 || n>=(int)pt.size()) throw new RuntimeException(); return pt.elementAt(n); } public void setPoint(int n,Point p)
22
23
Caso Comparativo con C, C++ y Java Para no alargar la exposicin slo estamos RuntimeException (para no tener que capturarla.) generando la excepcin
Al igual que en el caso C++, el mtodo isInterior() puede comentarse por partes para obtener las variantes 1 y 2. Ntese que de utilizarse el AWT, sera aconsejable renombrar esta clase pues ya hay una del mismo nombre en dicho paquete (o nos veramos forzados a usar el nombre calificado.)
Clase AGUtil
La clase AGUtil es tambin similar a la clase Util de nuestra versin C++. Las constantes del preprocesador son reemplazadas con variables "final". Ntese que hemos evitado el nombre "Util" a fin de no hacer conflicto con java.Util; de lo contrario tendramos que utilizar el nombre calificado haciendo el cdigo ms engorroso.
package com.americati.analgeom; public class AGUtil { final static long ALEATORIO_MAX=2147483647L; final static long IA=16807; final static long IQ=127773; final static long IR=2836; final static long AM=(long)(1.0/ALEATORIO_MAX); final static long MASK=123459876; private static long semilla=170374; private static long aleatorio() { long k,ans; semilla^=MASK; k=semilla/IQ; semilla=IA*(semilla-k*IQ)-IR*k; if(semilla<0) semilla+=ALEATORIO_MAX; ans=semilla; semilla^=MASK; return ans; } public static long aleatorio_entero(long minimo,long maximo) { return minimo+(int)((maximo-minimo+1.0)*aleatorio()/(ALEATORIO_MAX+1.0)); } public static double getTime() { return System.currentTimeMillis()/1000.0; } }
Resultados
24
Caso Comparativo con C, C++ y Java Las implementaciones arrojaron 294468 casos en los que los puntos quedaba al interior de los polgonos (de un total de 10000000 tests.) Consideramos de inters para el lector presentar los tiempos obtenidos en estas ejecuciones, sin embargo, debemos advertir que bajo ninguna circunstancia dichos valores representan resultados definitivos o un "benchmark" formal. Los resultados presentados corresponden a los mnimos tiempos obtenidos en un total de cinco ejecuciones por cada caso, y slo marginalmente hemos intentado evitar que otras aplicaciones distorsionen el consumo de recursos del sistema operativo. En [2] se puede hallar informacin ms completa para hacer un "benchmark" ms objetivo; los valores presentados pueden considerarse hasta cierto punto "anecdticos". En todos los casos se utiliz Linux Ubuntu Drake con kernel 2.6.15. El compilador de C y C++ fue GCC 4.0.3. Para el caso de Java, los resultados iniciales se obtuvieron con el JDK de Sun 1.5.0 R09; sin embargo, tambin se proporcionan resultados obtenidos con otras mquinas virtuales. Los tiempos (en segundos) se resumen en la tabla a continuacin.
Observaciones
Los tiempos obtenidos en relacin a las implementaciones de C y C++ son bastante razonables en la medida que el cdigo C++ contiene ms validaciones soportadas por el mismo lenguaje (encapsulacin) as como controles agregados como parte del cdigo (por ejemplo, ndices fuera de rango.) Claramente, el cdigo C++ resulta ms comprensible, ordenado y extensible, y de requerirse ms velocidad se podra sacrificar gradualmente la orientacin a objetos (quiz, hasta convertirlo en la versin en C.) Dos de los tiempos de Java son razonables si se toma en cuenta su ejecucin en "bytecodes", mayor control de excepciones en tiempo de ejecucin, y el "garbage collector"; sin embargo, el "90.56" resulta dificil de aceptar. Analizando la ejecucin (esto se puede hacer con herramientas de profiling) hallamos que el mtodo Point.insideTriangle() es el causante principal del retardo. El siguiente cdigo es una reescritura del mismo, la cual ciertamente tiene muy poco de "orientada a objetos":
boolean insideTriangle(Point A, Point B, Point C) { double ax=A.getX(),ay=A.getY(); double bx=B.getX(),by=B.getY(); double cx=C.getX(),cy=C.getY(); double PAx=ax-x, PAy=ay-y; double PBx=bx-x, PBy=by-y;
25
Como veremos, esta modificacin redujo el tiempo de la "variante 1" considerablemente. Esto ciertamente indica que la implementacin de las regiones "crticas" deben analizarse con cuidado, y sin temor a seguir enfoques poco ortodoxos. Nos pareci interesante contrastar este enfoque "no POO" (NPOO) con el original (al que llamaremos POO), utilizando las tres variantes y con diferentes mquinas virtuales (con bytecode generado con sus respectivos compiladores.) Los resultados se muestran a continuacin:
Sun 1.5.0 90.9 R9 Sun 6.0 R1 Ibm 5.0-4.0 Jrockit 1.5.0_06 81.1 109 71.2
Caso Comparativo con C, C++ y Java Considrese la figura A.1 en la cual tenemos el polgono (P0,P1,P2,P3,P4); si tomamos "P0" como origen, podemos subdividir el polgono en un conjunto de tringulos (recurdese que slo consideramos polgonos convexos.) Si podemos determinar si un punto "X" est al interior de cualquiera de estos tringulos, entonces el punto est al interior del polgono y el problema queda resuelto.
Como se puede apreciar, la recta que contiene al lado del tringulo divide al plano en dos regiones (semiplanos); es claro que en una de ellas se encuentra el tercer vrtice. Si ocurre (tal como en la figura) que el punto de anlisis est en el "semiplano opuesto", entonces podemos afirmar que dicho punto est al exterior del tringulo. Por el contrario, si el punto est en el mismo semiplano que el tercer vrtice, entonces no podemos afirmar nada al respecto; sin embargo, si hacemos este anlisis para los tres lados del tringulo, es claro que si nuestro punto es exterior, entonces deber necesariamente caer uno de los "semiplanos opuestos"; de lo contrario, es interior. Con esto hemos reducido el anlisis a determinar si el punto est en el semiplano del tercer vrtice o en el "semiplano opuesto"; dicho de otro modo, queremos saber si dos puntos estn del mismo lado de una recta, o en regiones opuestas.
Caso Comparativo con C, C++ y Java La figura A.3 ilustra el caso de dos puntos (A y X) que se encuentran en regiones opuestas a la recta determinada por (B,C). En la figura se ha trazado tambin un vector ortogonal a la recta (B,C) al que podemos llamar "N".
Ahora podemos trazar dos vectores desde A y X hacia cualquier punto de la recta (por ejemplo, hacia "C"). Estos dos vectores ("v1" y "v2") al proyectarse en "N" van a resultar en sentidos opuestos; sin embargo, si "X" estuviera del mismo lado que "A", entonces los vectores "v1" y "v2" al proyectarse resultaran con el mismo sentido. Una forma fcil de determinar el sentido de la proyeccin de un vector sobre otro, consiste en analizar el signo de su producto escalar:
Sean los productos escalares P1=v1.N y P2=v2.n, entonces si tienen signos opuestos tendremos P1.P2<0, en caso contrario, P1.P2>0. Esto proporciona un criterio de fcil clculo para determinar la posicin relativa, con lo que el problema inicial queda resuelto.
Segunda Variante
Con esta variante estamos intentando aligerar el las operaciones a fin de evitar realizar el anlisis antes descrito. Como se ver, no siempre podemos evitarlo, pero los resultados de las pruebas demuestran que vale la pena intentarlo. Si a partir del punto de anlisis trazamos ejes de coordenadas perpendiculares, entonces los puntos del polgono pueden quedar repartidos en diversos cuadrantes. Por ejemplo, si todos los puntos quedan en un nico cuadrante (cualquiera de los cuatro) es evidente que el punto es exterior al polgono (fig A.5-a.) De igual modo, si todos los puntos del polgono se ubican en dos cuadrantes adyacentes, entonces el punto tambin es exterior (fig A.5-b.) De igual modo, si los puntos del polgono 28
Caso Comparativo con C, C++ y Java se ubican en los cuatro cuadrantes, entonces necesariamente el punto ser interior (fig A.5-c.)
Esto nos permite determinar rpidamente la ubicacin del punto para ciertos casos. Lamentablemente, en ciertas situaciones tendremos que recurrir al anlisis de la primera variante. Por ejemplo, en la figura A.6 se muestran dos casos del polgono en tres cuadrantes:
Y la figura A.7 muestra dos casos del polgono en dos cuadrantes no adyacentes:
Son varios los casos a considerar (en total 16-1 pues se descarta el caso en el que no hay puntos del polgono), sin embargo, es extremadamente sencillo y eficiente el analizar los cuadrantes de los puntos (basta ver si sus coordenadas son mayores o menores que las del punto de anlisis.)
Tercera Variante
29
Caso Comparativo con C, C++ y Java Considrese la distancia mxima entre dos puntos cualesquiera del polgono. A esta distancia le llamaremos "dimetro" del polgono. Ahora consideremos la distancia que hay entre nuestro punto de anlisis y un punto arbitrario del polgono. Si esta distancia es mayor que el dimetro, es claro que el punto debe estar fuera del polgono; lamentablemente, si la distancia es menor entonces el punto puede estar dentro o fuera y debemos proceder a la segunda variante:
A fin de hacer eficiente este anlisis, ser necesario que cada polgono conserve el valor de su dimetro (lo puede calcular cada vez que se le agrega un nuevo punto) a fin de no tener que calcularlo repetidamente.
B. Referencias
[1] Press W. H. et. al. "Numerical Recipes in C: The Art of Scientific Computing" Cambridge University Press. 2 edition. 1992. [2] SPEC CPU2006 Run and Reporting Rules http://www.spec.org/cpu2006/Docs/ runrules.html
30