Está en la página 1de 14

Bueno, al llegar a este post asumo que ustedes ya han ledo acerca de lo que trata este

problema de la geometra computacional. Esto es muy importante puesto que algunos detalles
expuestos aqu no sern tratados muy profundamente. Pues bueno, vayamos al grano. El
algoritmo de graham fu uno de los primero algoritmos utilizados para resolver este problema.
El concepto fundamental de este algoritmo es tener en cuenta el sentido de giro que toma un
vector respecto a otro de forma iterativa. Para esto Graham nos dice lo siguiente:

Evidentemente es seudocdigo pero no hay mejor forma de entender esto que mediante una
prueba. A ver, empezemos.
Supongamos que se nos da un conjunto de puntos como los que se muestra en la figura
siguiente:

Y necesitamos hallar la cerradura convexa. Lo primero que nos dice Graham es que
escojamos el punto de menor ordenada y luego ordenemos el resto de puntos en funcin a su
ngulo que forman con la horizontal.

Ahora insertamos en una pila los tres primeros puntos de la nube de puntos(evidentemente ya
deben estar ordenados y el punto de menor ordenada es el que ocupa la menor posicin).
Hecho esto inicializamos un contador en 3 y empezamos las iteraciones.
Para i=3

Los tres puntos generan una vuelta la izquierda por lo que incrementamos el contador.
Entonces nuestro contador quedara as:
Para i=4

Vemos que forma una vuelta a la derecha por lo que el contador no se incrementar.
Para i=4

Ahora si se gener una vuelta la izquierda entonces nuestro contador se incrementa.


Para i=5

Tenemos otra vuelta a la izquierda y nuestro contador vuelve a incrementarse.


Para i=6

Una vuelta a la derecha. El contador no se incrementar.


Para i=6

Una vuelta a la izquierda, el contador se incrementar. Adems estamos viendo como varia el
contenido de la pila (la cual almacena los puntos que forman la cerradura convexa).
Para i =7

Una vuelta a la izquierda.


Para i=8

Vuleta a la derecha, contador sin variaciones.

Para i=8

Incrementamos el contador puesto que se gener una vuelta a la izquierda.

Para i=9

Incrementamos el contador.
Para i=10

Una vuelta a la derecha indica que el contador no se incrementar.


Para i=10

Incrementamos el contador.
Para i=11
Esta es la condicin de parada por lo que los datos en la pila quedan as:

Entonces con esos puntos construimos la cerradura convexa y nos quedara algo as:

Bueno, pero esto no estara completo sin una simulacin. El siguiente applet les mostrar
como trabaja esto:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.blogspot.rolandopalermo;
import
import
import
import
import
import
import
import
import
import
import

java.awt.BorderLayout;
java.awt.Canvas;
java.awt.Color;
java.awt.Graphics;
java.awt.Graphics2D;
java.awt.event.MouseEvent;
java.awt.geom.Point2D;
java.util.ArrayList;
java.util.List;
java.util.Stack;
javax.swing.JApplet;

/**
*
* @author Rolando
*/
public class Graham extends JApplet {
/**
* Initialization method that will be called after the applet
is loaded
* into the browser.
*/

private static Point2D pivote;


private Lienzo lienzo;
@Override
public void init() {
// TODO start asynchronous download of heavy resources
setLayout(new BorderLayout());
lienzo = new Lienzo();
lienzo.setSize(100, 100);
add(lienzo, BorderLayout.CENTER);
}

&&

public static List<Point2D> graham(List<Point2D> nube) {


Stack pila = new Stack();
List<Point2D> cerradura = new ArrayList<Point2D>();
List<Point2D> temp = new ArrayList<Point2D>();
//O(n)
pivote = nube.get(0);
for(Point2D p:nube) {
if(p.getY()<pivote.getY() || (p.getY()==pivote.getY()
p.getX()>pivote.getX())) {
pivote = p;
}
}
//O(nlog n)
temp = mergerSort(nube);
pila.push(temp.get(0));
pila.push(temp.get(1));
pila.push(temp.get(2));
int i = 3;
while(i<(temp.size())) {
Point2D t = (Point2D) pila.pop();
if(isLeft((Point2D) pila.peek(), t, temp.get(i))>0) {
pila.push(t);
pila.push(temp.get(i));
i++;
}
}
cerradura = pila.subList(0, pila.size());
return cerradura;
}

private static List<Point2D> mergerSort(List<Point2D> list) {


//
imprimirLista(list);
if(list.size()>1) {
int mitad = list.size()/2;
List<Point2D> A = mergerSort(list.subList(0, mitad));
List<Point2D> B = mergerSort(list.subList(mitad,
list.size()));
return merge(A,B);
} else {
return list;

}
}
private static List<Point2D> merge(List<Point2D> A,
List<Point2D> B) {
int temp1 = 0;
int temp2 = 0;
double angulo1;
double angulo2;
List<Point2D> temp = new ArrayList<Point2D>();
while((temp1<A.size()) && (temp2<B.size())) {
angulo1 = getAngulo(pivote, A.get(temp1));
angulo2 = getAngulo(pivote, B.get(temp2));
if(angulo1<=angulo2) {
temp.add(A.get(temp1));
temp1++;
} else {
temp.add(B.get(temp2));
temp2++;
}
}
while(temp1<A.size()) {
temp.add(A.get(temp1));
temp1++;
}
while(temp2<B.size()) {
temp.add(B.get(temp2));
temp2++;
}
return temp;
}
public static double getAngulo(Point2D a, Point2D b) {
double dx = b.getX() - a.getX();
double dy = b.getY() - a.getY();
double angulo = 0.0d;
if (dx == 0.0) {
if(dy == 0.0)
angulo = 0.0;
else if(dy > 0.0) angulo = Math.PI / 2.0;
else
angulo = (Math.PI * 3.0) / 2.0;
}
else if(dy == 0.0) {
if(dx > 0.0)
angulo = 0.0;
else
angulo = Math.PI;
}
else {
if(dx < 0.0)
angulo = Math.atan(dy/dx) + Math.PI;
else if(dy < 0.0) angulo = Math.atan(dy/dx) +
(2*Math.PI);
else
angulo = Math.atan(dy/dx);
}
return (angulo * 180) / Math.PI;

}
private static double isLeft(Point2D P0, Point2D P1, Point2D
P2) {
return (P1.getX() - P0.getX())*(P2.getY() - P0.getY()) (P2.getX() - P0.getX())*(P1.getY() - P0.getY());
}
// TODO overwrite start(), stop() and destroy() methods
class Lienzo extends Canvas {
private List<Point2D> nube = new ArrayList<Point2D>();
private List<Point2D> cerradura = new
ArrayList<Point2D>();
public Lienzo() {
super();
this.addMouseListener(new
java.awt.event.MouseAdapter(){
@Override
public void mousePressed(MouseEvent e){
mouseClic(e);
}
});
}
public void mouseClic(MouseEvent e) {
Point2D punto = convert(e.getX(), e.getY());
nube.add(punto);
repaint();
}
public void drawPoint(Point2D p, Graphics g2d) {
int[] screenCoordinates = convert(p);
g2d.drawRect(screenCoordinates[0]-2,
screenCoordinates[1]-2, 4, 4);
//
g2d.drawString(p.getX() + "," + p.getY(),
screenCoordinates[0], screenCoordinates[1]);
}
public void drawString(Point2D p, String string, Graphics
g2d) {
int[] screenCoordinates = convert(p);
g2d.drawString(string, screenCoordinates[0],
screenCoordinates[1]+15);
}
public void drawLine(Point2D inicio, Point2D fin, Graphics
g2d) {
int[] coor_i = convert(inicio);

int[] coor_f = convert(fin);


g2d.drawLine(coor_i[0], coor_i[1], coor_f[0],
coor_f[1]);
}
private int[] convert(Point2D p) {
return new int[]{
(getWidth() / 2) + (int)p.getX(),
(getHeight() / 2) - (int)p.getY()};
}
private Point2D convert(int screenX, int screenY) {
int x = screenX - (getWidth()/2);
int y = (getHeight()/2) - screenY;
return new Point2D.Double(x, y);
}
@Override
public void paint(Graphics g){
Graphics2D g2 = (Graphics2D) g;
int W = getWidth();
int H = getHeight();
g2.setColor(Color.white);
g2.fillRect(0, 0, W, H);
g2.setColor(Color.black.darker());
drawLine(new Point2D.Double(0, 0), new
Point2D.Double(0, (H/2)-10), g2);
drawLine(new Point2D.Double(0, 0), new
Point2D.Double((W/2)-10, 0), g2);
g2.setColor(Color.red);
for(Point2D p :nube) {
drawPoint(p,g2);
}
if(nube.size()>2) {
cerradura = graham(nube);
for(int i=0; i<cerradura.size(); i++) {
Point2D inicio = cerradura.get(i);
Point2D fin;
if(i==cerradura.size()-1) {
fin = cerradura.get(0);
} else {
fin = cerradura.get(i+1);
}
g.setColor(Color.blue.darker());
drawLine(inicio, fin, g2);
g.setColor(Color.green.darker());
drawPoint(inicio,g2);
drawPoint(fin,g2);
}
g.setColor(Color.black.darker());
drawPoint(pivote,g2);
drawString(pivote, "P0",g2);

}
}
}
}

También podría gustarte