lunes, octubre 18, 2010

Complejidad del algoritmo del camino más corto por fuerza bruta

Para probar la eficiencia del algoritmo de búsqueda de la ruta más corta entre dos puntos por fuerza bruta se requieren en primer lugar dos modificaciones a la clase Grafo: la creación de dos atributos (public String rutaMasCorta e int longituRutaMasCorta) donde se almacenarán respectivamente, la ruta más corta de recorrido del grafo en forma de cadena de caracteres y la longitud de dicha ruta; y en segundo lugar, se modifica el método recorrerRutas para que haga la comparación de cada ruta recorrida y guarde la ruta más corta y la distancia de dicha ruta en las variables mencionadas anteriormente, y adicionalmente para que no muestre cada ruta evaluada. El método queda así:

// recorre recursivamente las rutas entre un nodo inicial y un nodo final
    // almacenando en una cola cada nodo visitado
    private void recorrerRutas(int nodoI, int nodoF, Stack<Integer> resultado) {
        // si el nodo inicial es igual al final se evalúa la ruta en revisión
        if(nodoI==nodoF) {
            int respuesta = evaluar(resultado);
            if(respuesta < longitudMasCorta) {
                longitudMasCorta = respuesta;
                rutaMasCorta     = "";
                for(int x: resultado) rutaMasCorta+=(nodos[x]+" ");
            }
            return;
        }
        // Si el nodoInicial no es igual al final se crea una lista con todos los nodos
        // adyacentes al nodo inicial que no estén en la ruta en evaluación
        List<Integer> lista = new Vector<Integer>();
        for(int i=0; i<grafo.length;i++) {
            if(grafo[nodoI][i]!=0 && !resultado.contains(i))lista.add(i);
        }
        // se recorren todas las rutas formadas con los nodos adyacentes al inicial
        for(int nodo: lista) {
            resultado.push(nodo);
            recorrerRutas(nodo, nodoF, resultado);
            resultado.pop();
        }
    }

Una vez realizadas estas modificaciones, se crea una nueva clase de pruebas, TestGrafo, en la cual se van a crear grafos de tamaño 1 en adelante, aprovechando las letras del alfabeto romano, desde el nodo 'a' hasta el nodo 'z' en forma secuencial, se crearán las rutas para estos grafos y se realizará la búsqueda de la ruta más corta entre dos nodos aleatorios. A continuación se presenta el código completo en java de la clase TestGrafo:

import java.util.*;

public class TestGrafo {
    public static void main(String[] args) {
        // variables para cálculo del tiempo
        long      tiempo1;
        long      tiempo2;
        double    tiempo;
        String    cadena="";            // cadena con los posibles nodos
        Random    rnd = new Random();   // generador de números aleatorios
        final int MAX_DIST = 50;        // máxima distancia entre 2 nodos

        for(char letra='a'; letra<='z'; letra++) {
            cadena+=letra;
            // Crea un nuevo grafo con tantos nodos como letras en la cadena
            Grafo g = new Grafo(cadena);
            // Crea rutas de tamaño aleatorio entre todos los nodos
            for(char i='a';i<=letra;i++) {
                for(char j=(char)(i+1);j<=letra; j++) {
                    g.agregarRuta(i, j, rnd.nextInt(MAX_DIST));
                }
            }
            // Selecciona dos nodos aleatorios para buscar la ruta mas corta
            char origen  = cadena.charAt(rnd.nextInt(cadena.length()));
            char destino = cadena.charAt(rnd.nextInt(cadena.length()));
            // Tiempo del sistema antes del proceso
            tiempo1      = System.currentTimeMillis();
            // Busqueda de la ruta más corta entre los dos nodos aleatorios
            g.encontrarRutaMinimaFuerzaBruta(origen, destino);
            // Tiempo del sistema al finalizar el proceso
            tiempo2 = System.currentTimeMillis();
            // Tiempo del proceso
            tiempo  = (tiempo2-tiempo1)/1000.0;
            // Mostrar cantidad de nodos del grafo y tiempo transcurrido
            System.out.printf("%3d %,10.2f %n", cadena.length(), tiempo);
        }
    }
}

La ejecución del anterior programa trae los siguientes resultados para cantidad de nodos desde 1 hasta 13, con tiempos en segundos:

 1       0,01 
 2       0,00 
 3       0,00 
 4       0,00 
 5       0,00 
 6       0,00 
 7       0,02 
 8       0,00 
 9       0,13 
10       1,18 
11       9,92 
12      44,91 
13     564,72 

Siguiendo esta secuencia, se podría pensar que el tiempo de respuesta para 14 nodos podría estar en el orden de varias horas; puede notarse entonces que a partir de un número relativamente pequeño de nodos el tiempo para encontrar la solución se torna inmanejable. La razón de esto estriba en que la cantidad de rutas posibles que conectan los puntos de un grafo es proporcional al factorial del número de nodos en el grafo, por lo que el tiempo de ejecución de cualquier algoritmo que deba recorrer todos los nodos de un grafo será entonces del orden de O(N!).

domingo, octubre 17, 2010

Evaluador de Funciones con Constantes

En este versión del evaluador de funciones en Java se ha agregado la funcionalidad de usar las constantes definidas en la clase java.lang.Math (PI y E).

En el método main de ejemplo se crea una función que calcula el área de un círculo utilizando el valor de PI:


import java.util.*;
import java.util.regex.*;
import java.lang.reflect.*;

public class Expresion {
    enum TipoToken  {   NUMERO, VARIABLE, FUNCION, ADD, SUB, MUL, DIV,
                        EXP, P_IZQ, P_DER, ERROR };
    class Token {
        TipoToken tipo;
        String    texto;
        Token(TipoToken ti, String te) { tipo=ti; texto=te;}
        Token(TipoToken ti) { tipo = ti; }
    }
    Queue<Token>    colaTokens;
    String          cadenaFuncion;
    double          variableX;

    Expresion(String c) throws Exception {
        cadenaFuncion = c;
    }

    private void generarTokens() throws Exception {
        colaTokens = new LinkedList<Token>();
        StringBuffer entrada   = new StringBuffer(cadenaFuncion);
        Pattern pNumero = Pattern.compile("\\-?\\d+(\\.\\d+)?");
        Pattern pID     = Pattern.compile("\\p{Alpha}\\w+");
        Pattern pFuncion= Pattern.compile("\\p{Alpha}\\w+\\s+\\(");
        while(entrada.length()>0) {
            Matcher      m  = pNumero.matcher(entrada);
            if(m.lookingAt()) {
                colaTokens.add(new Token(TipoToken.NUMERO,m.group()));
                entrada.delete(0, m.end());
                continue;
            }
            if(entrada.charAt(0) == 'x' || entrada.charAt(0) == 'X') {
                colaTokens.add(new Token(TipoToken.VARIABLE,"x"));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '+') {
                colaTokens.add(new Token(TipoToken.ADD));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '-') {
                colaTokens.add(new Token(TipoToken.SUB));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '*') {
                colaTokens.add(new Token(TipoToken.MUL));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '/') {
                colaTokens.add(new Token(TipoToken.DIV));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '(') {
                colaTokens.add(new Token(TipoToken.P_IZQ));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == ')') {
                colaTokens.add(new Token(TipoToken.P_DER));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '^') {
                colaTokens.add(new Token(TipoToken.EXP));
                entrada.deleteCharAt(0);
                continue;
            }
            m = pFuncion.matcher(entrada);
            if(m.lookingAt()) {
                String cadena = m.group();
                cadena = cadena.split("\\s+\\(")[0].trim();
                colaTokens.add(new Token(TipoToken.FUNCION, cadena));
                continue;
            }
            m = pID.matcher(entrada);
            if(m.lookingAt()) {
                colaTokens.add(new Token(TipoToken.VARIABLE, m.group()));
                entrada.delete(0, m.end());
                continue;
            }
            throw new Exception("Elemento no reconocido en la entrada: "
                                + entrada.charAt(0));
        }
    }

    private double evaluar(double x) throws Exception {
        generarTokens();
        variableX = x;
        return expresion();
    }

    private double expresion() {
        double respuesta=termino();
        while(!colaTokens.isEmpty() ) {
            switch(colaTokens.element().tipo) {
                case ADD:   colaTokens.remove();
                            respuesta+=termino();
                            continue;
                case SUB:   colaTokens.remove();
                            respuesta-=termino();
                            continue;
            }
            break;
        }
        return respuesta;
    }

    private double termino() {
        double respuesta=factor();
        while(!colaTokens.isEmpty() ) {
            switch(colaTokens.element().tipo) {
                case MUL:   colaTokens.remove();
                            respuesta*=factor();
                            continue;
                case DIV:   colaTokens.remove();
                            respuesta/=factor();
                            continue;
                default:
            }
            break;
        }
        return respuesta;
    }

    private double factor() {
        double respuesta=valor();
        while(!colaTokens.isEmpty() ) {
            switch(colaTokens.element().tipo) {
                case EXP:   colaTokens.remove();
                            respuesta=Math.pow(respuesta,valor());
                            continue;
            }
            break;
        }
        return respuesta;
    }

    private double valor() {
        Token token;
        try {
            double respuesta = 0;
            token     = colaTokens.poll();
            switch(token.tipo) {
                case P_IZQ:     respuesta = expresion();
                                leaToken(TipoToken.P_DER);
                                return respuesta;
                case NUMERO:    return Double.parseDouble(token.texto);
                case VARIABLE:  if(token.texto.toLowerCase().equals("x")) {
                                    return variableX;
                                }
                                Field f = java.lang.Math.class.
                                        getField(token.texto);
                                return f.getDouble(null);
                case FUNCION:   double argumento=expresion();
                                leaToken(TipoToken.P_DER);
                                Method m = java.lang.Math.class.
                                           getMethod(token.texto, Double.TYPE);
                                return (Double) m.invoke(null, argumento);
            }
            return respuesta;
        }
        catch(Exception ex) {
            System.err.println("Error: " + ex.getMessage());
            System.exit(0);
        }
        return 0;
    }

    private boolean leaToken(TipoToken t) {
        Token token = colaTokens.poll();
        if(token.tipo.equals(t)) {
            return true;
        }
        else {
            System.err.println("Error: elemento no permitido " + token.texto );
            return false;
        }
    }

    public static void main(String[] args) throws Exception {
        String funcion = "PI*x^2" ;
        Expresion  exp = new Expresion(funcion);
        for(int x=0; x<=10; x++) {
            System.out.println(x + " -> " + exp.evaluar(x));
        }
    }
}

viernes, octubre 15, 2010

Ruta más corta - Solución por fuerza bruta

El problema de encontrar la ruta más corta entre dos puntos se puede abordar por diferentes enfoques de programación. La solución más obvia es la de evaluar todas las posibles rutas para luego compararlas y encontrar la más corta. Si bien esta solución puede encontrar la ruta más corta (o más larga) en forma absoluta, su implementación cuando la cantidad de nodos a evaluar es relativamente alta es demasiado costosa computacionalmente hablando porque la cantidad de rutas tiende a crecer exponencialmente.

Una posible implementación de esta evaluación de todas las rutas por fuerza bruta se presenta a continuación. El programa tiene dos partes, en la primera de ellas está la definición de una clase Grafo, con los métodos para la creación de los nodos y la asignación de rutas entre dos puntos y la distancia entre cada punto. Cada nodo puede ser identificado con una letra o caracter y, para efectos de simplificar el proceso estas denominaciones se envían al constructor del grafo en una cadena de caracteres. El grafo se representa por una matriz cuadrada donde cada elemento de la matriz representa la distancia entre el nodo fila y el nodo columna respectivo. Se han obviado las comprobaciones para dar mayor claridad al código:

import java.util.*;

public class Grafo {
    int[][] grafo;
    char[]  nodos;

    Grafo(String serieNodos) {
        nodos = serieNodos.toCharArray();
        grafo = new int[nodos.length][nodos.length];
    }

    // asigna el tamaño de la arista entre dos nodos
    public void agregarRuta(char origen, char destino, int distancia) {
        int n1 = posicionNodo(origen);
        int n2 = posicionNodo(destino);
        grafo[n1][n2]=distancia;
        grafo[n2][n1]=distancia;
    }

    // retorna la posición en el arreglo de un nodo específico
    private int posicionNodo(char nodo) {
        for(int i=0; i<nodos.length; i++) {
            if(nodos[i]==nodo) return i;
        }
        return -1;
    }

    // encuentra la ruta mínima entre dos nodos del grafo
    public void encontrarRutaMinimaFuerzaBruta(char inicio, char fin) {
        int p1 = posicionNodo(inicio);
        int p2 = posicionNodo(fin);
        // cola para almacenar cada ruta que está siendo evaluada
        Stack<Integer> resultado = new Stack<Integer>();
        resultado.push(p1);
        recorrerRutas(p1, p2, resultado);
    }

    // recorre recursivamente las rutas entre un nodo inicial y un nodo final
    // almacenando en una cola cada nodo visitado
    private void recorrerRutas(int nodoI, int nodoF, Stack<Integer> resultado) {
        // si el nodo inicial es igual al final se muestra y evalúa la ruta en revisión
        if(nodoI==nodoF) {
            for(int x: resultado) System.out.print(nodos[x]+ " ");
            System.out.print(": " + evaluar(resultado));
            System.out.println();
            return;
        }
        // Si el nodoInicial no es igual al final se crea una lista con todos los nodos
        // adyacentes al nodo inicial que no estén en la ruta en evaluación
        List<Integer> lista = new Vector<Integer>();
        for(int i=0; i<grafo.length;i++) {
            if(grafo[nodoI][i]!=0 && !resultado.contains(i))lista.add(i);
        }
        // se recorren todas las rutas formadas con los nodos adyacentes al inicial
        for(int nodo: lista) {
            resultado.push(nodo);
            recorrerRutas(nodo, nodoF, resultado);
            resultado.pop();
        }
    }

    // evaluar la longitud de una ruta
    public int evaluar(Stack<Integer> resultado) {
        int  resp = 0;
        int[]   r = new int[resultado.size()];
        int     i = 0;
        for(int x: resultado) r[i++]=x;
        for(i=1; i<r.length; i++) resp+=grafo[r[i]][r[i-1]];
        return resp;
    }

    public static void main(String[] args) {
        Grafo g = new Grafo("abcdef");
        g.agregarRuta('a','b', 5);
        g.agregarRuta('a','e', 3);
        g.agregarRuta('b','e', 6);
        g.agregarRuta('b','f', 9);
        g.agregarRuta('b','c', 10);
        g.agregarRuta('c','d', 15);
        g.agregarRuta('c','f', 9);
        g.agregarRuta('d','e', 1);
        g.agregarRuta('d','f', 2);
        g.agregarRuta('e','f', 12);
        char inicio = 'a';
        char fin    = 'd';
        g.encontrarRutaMinimaFuerzaBruta(inicio, fin);
    }
}

jueves, octubre 14, 2010

Generar combinaciones de caracteres

Generación con sustitución

Si se quieren generar todas las posibles cadenas que se forman con un determinado número de caracteres, una primera estrategia es hacer un programa con ciclos anidados, uno para cada posición de la cadena que se debe generar. Un ejemplo de esta estrategia es el siguiente:


// Generar secuencias de 4 caracteres
public static void generar4Caracteres(char[] elementos) {
        String r;
        for (int i = 0; i < elementos.length; i++) {
            for (int j = 0; j < elementos.length; j++) {
                for (int k = 0; k < elementos.length; k++) {
                    for (int l = 0; l < elementos.length; l++) {
                        r=""+elementos[i]+elementos[j]+elementos[k]+elementos[l];
                        // Acá se debe hacer algo con la cadena generada
                        System.out.println(r);
                    }
                }
            }
        }
    }


La principal limitación del anterior método es que solo se generan cadenas de 4 caracteres, y en caso de requerir cadenas generadas de otro tamaño, se requiere un nuevo método, pues la cantidad de caracteres de la cadena generada es la que determina el número de ciclos for del proceso. Una mejor estrategia para generar una cadena de N caracteres es, comenzando con la cadena vacía, agregar un caracter y llamar recursivamente el mismo método para generar las cadenas de tamaño N-1. Cuando la cadena a generar sea de tamaño 0, es porque ya se completó la generación de una ocurrencia y se puede proceder a procesar la cadena generada. En este caso el método queda así:


// Permutaciones con sustitución
    public static void generarPermutacionSust(char[] elementos, String actual, int cantidad) {
        if(cantidad==0) {
            // Hacer con la secuencia generada
            System.out.println(actual);
        }
        else {
            for(int i=0; i<elementos.length; i++) {
                generarPermutacionSust(elementos, actual elementos[i],cantidad-1);
            }
        }
    }


Para probar este método para generar todas las posibles subcadenas de dos caracteres con sustitución formadas con los dígitos 1, 2 y 3, se puede utilizar este segmento de instrucciones:


String alfabeto = "123";
char[] elementos = alfabeto.toCharArray();
generarPermutacionSust(elementos, "", 2);


Con este código se generarían como salida las siguientes cadenas: 11, 12, 13, 21, 22, 23, 31, 32 y 33


Generación sin sustitución

Cuando se requieren generar todas las posibles cadenas de determinado tamaño formadas a partir de un conjunto de caracteres pero sin que se repita ningún caracter entonces se debe llevar un pequeño control de que caracteres ya han salido para evitar su inclusión.

En este programa basado en la entrada anterior anterior del blog se agrega un arreglo de valores booleanos de 100 posiciones, aunque podría ser de mayor tamaño, en el cual cada vez que se utiliza un caracter, se marca su posición para no volverlo a usar, pero se deja disponible una vez se ha liberado el caracter de su uso:

// Permutaciones sin sustitución
    static boolean[] control = new boolean[100];
    public static void generarPermutacionNoSust(char[] elementos, String actual, int cantidad) {
        if(cantidad==0) {
            // Hacer con la secuencia generada
            System.out.println(actual);
        }
        else {
            for(int i=0; i<elementos.length; i  ) {
                if(control[i]==true) continue;
                control[i]=true;
                generarPermutacionNoSust(elementos, actual elementos[i],cantidad-1);
                control[i]=false;
            }
        }
    }


Para probar este método para generar todas las posibles subcadenas de dos caracteres sin sustitución formadas con los dígitos 1, 2 y 3, se puede utilizar este segmento de instrucciones:


String alfabeto = "123";
char[] elementos = alfabeto.toCharArray();
generarPermutacionNoSust(elementos, "", 2);


Con este código se generarían como salida las siguientes cadenas: 12, 13, 21, 23, 31 y 32

martes, octubre 12, 2010

Mejorando la generación de números vampiros

En la siguiente versión del método generarVampiros, se va a modificar la salida con tres parámetros booleanos que permitirán generar hasta 8 listas diferentes teniendo en cuenta si los factores que se multiplican para formar el número vampiro tienen el mismo número de dígitos (vMitad); si dichos factores son números primos o no (vPrimo); y si tanto los factores como el número generado no tienen dígitos repetidos (vUnico).

De esta manera, la ejecución del método generarVampiros(8, true, true, true) generará solamente números vampiros de 8 dígitos formados por la multiplicación de dos números de 4 dígitos que sean primos y que no contengan dígitos repetidos. En este caso, la respuesta del programa es la siguiente:

10.349.527 = 2.579 x 4.013
10.429.753 = 2.309 x 4.517
17.204.359 = 2.309 x 7.451
18.647.023 = 2.741 x 6.803
54.918.067 = 5.801 x 9.467
64.781.293 = 7.691 x 8.423



public static Vector<String> generarVampiros(int digitos, boolean vMitad, 
                                             boolean vPrimo, boolean vUnico) {
        // vMitad: true para factores del mismo tamaño
        // vPrimo: true para factores primos
        // vUnico: true para factores sin dígitos repetidos
        Vector<Integer> va, vb, vc;
        Vector<String>  respuesta = new Vector<String>();
        long numFinal = (long) Math.pow(10,digitos)-1;    // último vampiro posible
        long numBase  = (long) Math.pow(10,digitos-1);    // primer vampiro posible
        long numMin   = (long) Math.pow(10,digitos/2-1);  // menor factor tamaño n/2
        long numMax   = (long) Math.pow(10,digitos/2)-1;  // mayor factor tamaño n/2
        long factorInicialA=0;
        long factorFinalA=0;
        long factorInicialB=0;
        long factorFinalB=0;
        if(vMitad) {
            factorInicialA = numMin;
            factorFinalA   = numMax;
        }
        else {
            factorInicialA = 1;
            factorFinalA   = numFinal;
        }
        for(long a = factorInicialA; a<=factorFinalA; a++) {
            if(vPrimo && !Algoritmos.esPrimoV6(a)) continue;
            va = vectorDigitos(a);
            if(vMitad) {
                factorInicialB=Math.max(a+1,numBase/a);
                factorFinalB=numMax;
            }
            else {
                factorInicialB = Math.max(a+1,numBase/a);
                factorFinalB = numFinal;
            }
            for(long b = factorInicialB; b<=factorFinalB; b++) {
                if(vPrimo && !Algoritmos.esPrimoV6(b)) continue;
                long c = a*b;
                if(c>numFinal) { b=numFinal; continue; }
                if(c<numBase) continue;
                vb = vectorDigitos(b);
                vc = vectorDigitos(c);
                if(vUnico) {
                    Set<Integer> sa = new TreeSet<Integer>();
                    Set<Integer> sc = new TreeSet<Integer>();
                    sa.addAll(va);
                    sa.addAll(vb);
                    sc.addAll(vc);
                    if(sc.size()==digitos && sa.equals(sc)) {
                        respuesta.add(String.format("%,12d = %,6d x %,6d",c, a,b));
                    }
                }
                else {
                    for(int x: va) vb.add(x);
                    Collections.sort(vb);
                    Collections.sort(vc);
                    if(vb.equals(vc)) {
                        respuesta.add(String.format("%,12d = %,6d x %,6d",c, a,b));
                    }
                }
            }
        }
        Collections.sort(respuesta);
        return respuesta;
    }

domingo, octubre 10, 2010

Evaluador simple de funciones en java

Escribir un programa para evaluar una función que sea ingresada por el usuario como una cadena de caracteres es una labor que exige utilizar los pasos principales del proceso de compilación: análisis léxico, análisis sintáctico y análisis semántico.

En el programa en java que se transcribe a continuación, se define una clase llamada Expresion que puede ser incorporada fácilmente a cualquier otro programa, pues no requiere clases especiales diferentes a las ya existentes en la máquina virtual de java. Se requiere tan solo un uso reducido del paquete java.util.regex para el manejo de expresiones regulares, y del paquete java.lang.reflect para la invoación de métodos de la clase java.lang.Math como parte de la función ingresada por el usuario.

Los objetos de la clase Expresion se crean con una cadena que representa la función, la cual podría ser, por ejemplo "x^3-2*x^2+1", o incluso utilizar funciones matemáticas de la clase java.lang.Math que reciban un solo valor como parámetro, por lo que la función x*cos(x)^2 sería completamente válida.

import java.util.*;
import java.util.regex.*;
import java.lang.reflect.*;

public class Expresion {
    enum TipoToken  {   NUMERO, VARIABLE, FUNCION, ADD, SUB, MUL, DIV,
                        EXP, P_IZQ, P_DER, ERROR };
    class Token {
        TipoToken tipo;
        String    texto;
        Token(TipoToken ti, String te) { tipo=ti; texto=te;}
        Token(TipoToken ti) { tipo = ti; }
    }
    Queue<Token>    colaTokens;
    String          cadenaFuncion;
    double          variable;

    Expresion(String c) throws Exception {
        cadenaFuncion = c;
    }

    private void generarTokens() throws Exception {
        colaTokens = new LinkedList<Token>();
        StringBuffer entrada   = new StringBuffer(cadenaFuncion);
        Pattern pNumero = Pattern.compile("\\-?\\d+(\\.\\d+)?");
        Pattern pID     = Pattern.compile("\\p{Alpha}\\w+");
        while(entrada.length()>0) {
            Matcher      m  = pNumero.matcher(entrada);
            if(m.lookingAt()) {
                colaTokens.add(new Token(TipoToken.NUMERO,m.group()));
                entrada.delete(0, m.end());
                continue;
            }
            if(entrada.charAt(0) == 'x' || entrada.charAt(0) == 'X') {
                colaTokens.add(new Token(TipoToken.VARIABLE,"x"));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '+') {
                colaTokens.add(new Token(TipoToken.ADD));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '-') {
                colaTokens.add(new Token(TipoToken.SUB));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '*') {
                colaTokens.add(new Token(TipoToken.MUL));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '/') {
                colaTokens.add(new Token(TipoToken.DIV));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '(') {
                colaTokens.add(new Token(TipoToken.P_IZQ));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == ')') {
                colaTokens.add(new Token(TipoToken.P_DER));
                entrada.deleteCharAt(0);
                continue;
            }
            if(entrada.charAt(0) == '^') {
                colaTokens.add(new Token(TipoToken.EXP));
                entrada.deleteCharAt(0);
                continue;
            }
            m = pID.matcher(entrada);
            if(m.lookingAt()) {
                colaTokens.add(new Token(TipoToken.FUNCION, m.group()));
                entrada.delete(0, m.end());
                continue;
            }
            throw new Exception("Elemento no reconocido en la entrada: " 
                                 + entrada.charAt(0));
        }
    }

    private double evaluar(double x) throws Exception {
        generarTokens();
        variable = x;
        return expresion();
    }

    private double expresion() {
        double respuesta=termino();
        while(!colaTokens.isEmpty() ) {
            switch(colaTokens.element().tipo) {
                case ADD:   colaTokens.remove();
                            respuesta+=termino();
                            continue;
                case SUB:   colaTokens.remove();
                            respuesta-=termino();
                            continue;
            }
            break;
        }
        return respuesta;
    }

    private double termino() {
        double respuesta=factor();
        while(!colaTokens.isEmpty() ) {
            switch(colaTokens.element().tipo) {
                case MUL:   colaTokens.remove();
                            respuesta*=factor();
                            continue;
                case DIV:   colaTokens.remove();
                            respuesta/=factor();
                            continue;
                default:
            }
            break;
        }
        return respuesta;
    }

    private double factor() {
        double respuesta=valor();
        while(!colaTokens.isEmpty() ) {
            switch(colaTokens.element().tipo) {
                case EXP:   colaTokens.remove();
                            respuesta=Math.pow(respuesta,valor());
                            continue;
            }
            break;
        }
        return respuesta;
    }

    private double valor() {
        Token token;
        try {
            double respuesta = 0;
            token     = colaTokens.poll();
            switch(token.tipo) {
                case P_IZQ:     respuesta = expresion();
                                leaToken(TipoToken.P_DER);
                                return respuesta;
                case NUMERO:    return Double.parseDouble(token.texto);
                case VARIABLE:  return variable;
                case FUNCION:   leaToken(TipoToken.P_IZQ);
                                double argumento=expresion();
                                leaToken(TipoToken.P_DER);
                                Method m = java.lang.Math.class.
                                           getMethod(token.texto, Double.TYPE);
                                return (Double) m.invoke(null, argumento);
            }
            return respuesta;
        }
        catch(Exception ex) {
            System.err.println("Error: "  + ex.getMessage());
            System.exit(0);
        }
        return 0;
    }

    private boolean leaToken(TipoToken t) {
        Token token = colaTokens.poll();
        if(token.tipo.equals(t)) {
            return true;
        }
        else {
            System.err.println("Error: elemento no permitido "  + token.texto );
            return false;
        }
    }

    public static void main(String[] args) throws Exception {
        String funcion = "x*cos(x)^2";
        Expresion  exp = new Expresion(funcion);
        for(int x=0; x<=10; x++  ) {
            System.out.println(exp.evaluar(x));
        }
    }
}

El método main es una muestra del uso que se le puede dar a esta clase. Es importante tener en cuenta que sólo acepta funciones definidas con la variable 'x' y que las funciones matemáticas de la clase java.lang.Math aceptadas son las que reciben un solo parametro como argumento. Sin embargo, es válido utilizar por ejemplo la fución "sin(cos(x^2+1))", ya que cada una de las funciones está recibiendo como argumento una función o expresión evaluable de un solo argumento.

viernes, octubre 08, 2010

Los 5 lenguajes de programación más populares - Octubre 2010

De acuerdo con la información del portal tiobe.com los lenguajes de programación más populares entre la comunidad informática mundial para el mes de octubre de 2010 son los siguientes:

1. Java (18.17%)
2. C (17.18%)
3. C++ (9.80%)
4. PHP (8.32%)
5. VisualBasic (5.65%)

Es importante anotar que, a pesar de la inmensa cantidad de lenguajes de programación existentes, estos 5 concentran (y lo han hecho por mucho tiempo) aproximadamente el 60% del interés de la comunidad informática mundial.

En cuanto a la evolución histórica de preferencias de lenguajes de programación java se mantiene en el primer lugar, pero es muy importante tener en cuenta que el lenguaje C mantiene una vigencia constante desde el año 1985.

Por tipos de lenguajes de programación, los lenguajes orientados a objetos tienen el 55.9% de las preferencias y los lenguajes procedimentales el 38.9%, dejando sólo el 5.2% para los demás tipos de lenguajes de programación, lo que indica que si bien puede existir un interés de tipo académico o investigativo por otros paradigmas de programación, los nichos donde se pueden aplicar conocimientos en estos lenguajes son reducidos.



Multiprocesamiento recursivo en JAVA 7

Una de las estrategias de diseño de algoritmos más comunes es la de "divide y vencerás", en la cual, un problema de tamaño relativ...