Lunes, 01 Agosto 2016 00:00

Valor de una función matemática en Java

Escrito por 
Valora este artículo
(0 votos)

Para trabajos de programación aplicada, como por ejemplo para cálculos numéricos, aveces hacemos uso de funciones matematicas, ya sean lineales,  cuadráticas, cubicas, trigonométricas etc. Y nos encontramos en la necesidad de poder analizar en valor de una función especifica, en el caso  de ser una solo función no habría problemas, pero en el caso de ser cualquier función nos encontramos en la necesidad de quizás expresar la función de la forma mas conocida.

Como por ejemplo para funciones cuatraticas la podríamos escribir del siguiente modo:

                     yf(x)=ax2+bx+c
En un programa la escribiríamos del siguiente modo
                    Yf(x)=ax ^2+bx+c
O bien para una función trigonométrica     
                    Yf(x)=sen(ax ^2)+bx+tan c

Para escribirlas de ese modo necesitamos una clase o algo parecido, que nos pueda aceptar tal expresión y poder calcular el valor de la función según el valor de x que le demos.
En este post se describirá una clase que hace tal tarea, osea podemos enviarle a la clase una expresión como la anterior, con un valor de x, y que la misma me realice el calculo. El principal problemas que no encontramos  es hacer que java nos reconozca un String  que contenga una operacion y luego nos calcule esa operacion,  una de las formas de realizarlo es aprovechando la capacidad que tiene java de poder ejecutar codigo Java Script (aqui hablamos un poco de esto http://compujuy.com.ar/blogs/item/40-javascript-java)

Osea  basicamente lo que haremos en pasos es

  1. Insertar una funcion matematica de la forma conociada (por ejemplo sen 2x)
  2. Parsear esa funcion conocida a  una operacion Java Script
  3. Ejecutar la operacion Java Script desde Java

El código a continuación es el código de la clase “Funcion”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package ar.com.compujuy.funciones;
 
import java.util.ArrayList;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
 
/**
 *
 * @author rafael
 */
public class Funcion {
 
    private String operacion;
    private String resultadoConversion;
    private String resultadoOperacion;
    private int indiceIni;
    private int indiceFin;
    private String valor;
 
    public Funcion() {
    }
 
    public String evaluar(String operacion, String valor) {
        this.operacion = operacion;
        this.valor = valor;
        this.resultadoConversion = analizaCadena(this.operacion);
        this.resultadoConversion = reemplazaOperacionJS(this.resultadoConversion);
        return this.resultadoOperacion = calculo(this.resultadoConversion);
    }
 
    private String analizaCadena(String cadena) {
        cadena = cadena.replaceAll("x", this.valor);
        cadena = quitarEspacios(cadena);
        cadena = "?" + cadena;
        char[] vectorCadena = cadena.toCharArray();
        if (cadena.contains("^")) {
            cadena = reemplazaPotencia(vectorCadena, cadena);
        }
        vectorCadena = cadena.toCharArray();
        if (cadena.contains("cos")) {
            cadena = reemplazaTrigonometrica(vectorCadena, cadena, ".co", 'c');
        }
        vectorCadena = cadena.toCharArray();
        if (cadena.contains("sen")) {
            cadena = reemplazaTrigonometrica(vectorCadena, cadena, ".si", 's');
        }
        vectorCadena = cadena.toCharArray();
        if (cadena.contains("tan")) {
            cadena = reemplazaTrigonometrica(vectorCadena, cadena, ".ta", 't');
        }
 
        return cadena;
    }
 
    private String reemplazaParIzq(char[] cadena, int indice) {
        ArrayList<Character> lista1 = new ArrayList<Character>();
        ArrayList<Character> lista2 = new ArrayList<Character>();
        String res = "";
        int i;
        for (i = indice - 1; i >= 0; i--) {
            if (cadena[i] == ')') {
                lista1.add(cadena[i]);
            } else if (cadena[i] == '(') {
                lista2.add(cadena[i]);
            }
            res = cadena[i] + res;
            if ((lista1.size() == lista2.size()) && (i != (indice - 1))) {
                this.indiceIni = i;
                return res;
            }
        }
        return null;
    }
 
    private String reemplazaParDer(char[] cadena, int indice) {
        ArrayList<Character> lista1 = new ArrayList<Character>();
        ArrayList<Character> lista2 = new ArrayList<Character>();
        String res = "";
        int i;
        for (i = indice + 1; i < cadena.length; i++) {
            if (cadena[i] == '(') {
                lista1.add(cadena[i]);
            } else if (cadena[i] == ')') {
                lista2.add(cadena[i]);
            }
            res += cadena[i];
            if ((lista1.size() == lista2.size()) && (i != (indice + 1))) {
                this.indiceFin = i + 1;
                return res;
            }
        }
        return null;
    }
 
    private String reemplazaNumIzq(char[] cadena, int indice) {
        String resultadoBase = "";
        String res = "";
        for (int i = indice - 1; i >= 0; i--) {
            res = resultadoBase;
            res += cadena[i];
            if (isNumeric(res)) {
                resultadoBase = cadena[i] + resultadoBase;
                this.indiceIni = i;
            } else {
                break;
            }
        }
        return resultadoBase;
    }
 
    private String reemplazaNumDer(char[] cadena, int indice) {
        String resultadoBase = "";
        String res = "";
        for (int i = indice + 1; i < cadena.length; i++) {
            res = resultadoBase;
            res += cadena[i];
            if (isNumeric(res)) {
                resultadoBase += cadena[i];
                this.indiceFin = i + 1;
            } else {
                break;
            }
        }
        return resultadoBase;
    }
 
    private boolean isNumeric(String str) {
        if (!str.equals(".")) {
            try {
                Double.parseDouble(str);
            } catch (NumberFormatException nfe) {
                return false;
            }
            return true;
        } else {
            return true;
        }
    }
 
    private String reemplazaPotencia(char[] vectorCadena, String cadena) {
        String resIzq = "", resDer = "";
        for (int indice = 0; indice < vectorCadena.length; indice++) {
            if (vectorCadena[indice] == '^') {
                if (vectorCadena[indice - 1] == ')') {
                    resIzq = reemplazaParIzq(vectorCadena, indice);
                } else {
                    resIzq = reemplazaNumIzq(vectorCadena, indice);
                }
                if (vectorCadena[indice + 1] == '(') {
                    resDer = reemplazaParDer(vectorCadena, indice);
                } else {
                    resDer = reemplazaNumDer(vectorCadena, indice);
                }
 
                vectorCadena = (cadena.substring(0, this.indiceIni) + ".po("
                        + resIzq + "," + resDer + ")" + (cadena.substring(this.indiceFin, cadena.length()))).toCharArray();
                cadena = (cadena.substring(0, this.indiceIni) + ".po("
                        + resIzq + "," + resDer + ")" + (cadena.substring(this.indiceFin, cadena.length())));
                indice = 0;
            }
        }
        return cadena;
    }
 
    private String reemplazaTrigonometrica(char[] vectorCadena, String cadena, String operacion, char caracter) {
        String resDer = "";
        for (int indice = 0; indice < vectorCadena.length; indice++) {
            if ((vectorCadena[indice] == caracter) && ((vectorCadena)[indice - 1] != '.') && (indice != 0)) {
                if (vectorCadena[indice + 3] == '(') {
                    resDer = reemplazaParDer(vectorCadena, indice + 2);
                } else {
                    resDer = reemplazaNumDer(vectorCadena, indice + 2);
                }
 
                vectorCadena = (cadena.substring(0, indice) + operacion + "("
                        + resDer + ")" + (cadena.substring(this.indiceFin, cadena.length()))).toCharArray();
                cadena = (cadena.substring(0, indice) + operacion + "("
                        + resDer + ")" + (cadena.substring(this.indiceFin, cadena.length())));
                indice = 0;
            }
        }
        return cadena;
    }
 
    public String getResultadoConversion() {
        return this.resultadoConversion;
    }
 
    public String getResultadoOperacion() {
        return resultadoOperacion;
    }
 
    private String quitarEspacios(String sTexto) {
        String sCadenaSinBlancos = "";
        for (int x = 0; x < sTexto.length(); x++) {
            if (sTexto.charAt(x) != ' ') {
                sCadenaSinBlancos += sTexto.charAt(x);
            }
        }
        return sCadenaSinBlancos;
    }
 
    private String reemplazaOperacionJS(String operacion) {
        this.resultadoConversion = operacion.replaceAll(".po", "Math.pow");
        this.resultadoConversion = this.resultadoConversion.replaceAll(".co", "Math.cos");
        this.resultadoConversion = this.resultadoConversion.replaceAll(".si", "Math.sin");
        this.resultadoConversion = this.resultadoConversion.replaceAll(".ta", "Math.tan");
        return this.resultadoConversion.substring(1, this.resultadoConversion.length());
    }
 
    private String calculo(String cadena) {
        ScriptEngineManager script = new ScriptEngineManager();
        ScriptEngine js = script.getEngineByName("JavaScript");
        try {
            return js.eval(cadena).toString();
        } catch (Exception e) {
            return e.toString();
        }
    }
}

 

Procederemos a explicar sencillamente y grandes rasgos lo que hace cada método :

 public String evaluar( String operacion, String valor)

 Método que evalua la cadena "operacion" que contiene la funcion a analizar con el valor "valor", este al igual que el metodo acceso de la propiedad resultado

 "getResultado" nos devuelve el valor de la funcion matematica expresada en "operacion"

 

private String analizaCadena(String cadena)

Método que me analiza la cadea recibida del constructor, en este se evalua el contenido de la misma, es decir si existe una potencia, o una función trigonométrica.

 

private String reemplazaParIzq(char[] cadena,int indice)

Recibe un array conteniendo la operación y a partir del índice recibido de evalua hasta donde es el alcance de la operación contenida dentro de los parentesis.
Es decir si recibe una array conteniendo  (sen (1)-2+3) ^8  devuelve (sen(1)2+3)

 

private String reemplazaParDer(char[] cadena,int indice )

Igual a la anterior pero evalua el alcance pero por la derecha, es decir en una potencia devuelve el exponente.

 

private String reemplazaNumIzq(char[] cadena,int indice)

 En el caso de no haber paréntesis, evalua el alcance pero de los números que contiene la operación, por ejemplo si la operación es 233.4^8 devuelve 233.4

 

private String reemplazaNumDer(char[] cadena,int indice)

 Igual a la anterior pero devuelve el alcance de la operación por la derecha.

 

private String reemplazaPotencia(char[] vectorCadena,String cadena)

Si existe una potencia, este método es el indicado para realizar la conversión,es decir
Si en la cadena existe 233.4^8 lo reemplaza  por .po(233.4,8) para ser evaluada luego.

 

private String reemplazaTrigonometrica(char[] vectorCadena,String cadena,String operacion,char caracter)

Reemplaza las funciones trigonométricas, es decir si en la operación llega sen (3), la reemplaza por
.si(3), para luego ser evaluada.

 

private String  quitarEspacios(String sTexto)

Quita espacios en blanco.

 

private String reemplazaOperacionJS(String operacion)

para este caso Utilizamos la libreria de JavaScript incorporada en java, asi que reemplazamos para que este lenguaje la pueda analizar, como ser si viene en la cadena
.si (3), lo reemplazamos por Math.sin(3)

 

private String calculo(String cadena)

una vez analizada toda la cadena, se procede con este metodo a realizar el calculo, para ello hacemos uso de javascript (ver este post para mas informacion).
La sintaxis para utilizar esta clase es la siguiente

Ahora crearemos un proyecto Java en Netbeans,y en nuestro Main incluimos las siguientes líneas para probar las funciones escritas anteriormente.
En todos los ejemplos observamos que instanciamos la clase Funcion, llamamos el metodo evaluar, y luego mostramos por la salida estándar  la conversión que realiza la clase y el valor de la función.
El constructor es inicializado, con la cadena que contiene la operación, mas el valor que le vamos a dar al valor de x.
Probamos la potencia

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package funciones;
 
import ar.com.compujuy.funciones.Funcion;
import javax.script.ScriptException;
 
/**
 *
 * @author rafa22
 */
public class Funciones {
 
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws ScriptException{
      String cadena="3^x+32+4^(2+x)";//
      Funcion f=new Funcion();
      f.evaluar(cadena, "2");
      System.out.println(f.getResultadoConversion());
      System.out.println(f.getResultadoOperacion());
    }
 
}

El resultado

Probamos la raíz

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package funciones;
 
import ar.com.compujuy.funciones.Funcion;
import javax.script.ScriptException;
 
/**
 *
 * @author rafa22
 */
public class Funciones {
 
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws ScriptException{
      String cadena="x^(1/2)";
      Funcion f=new Funcion();
      f.evaluar(cadena, "144");
      System.out.println(f.getResultadoConversion());
      System.out.println(f.getResultadoOperacion());
    }
 
}

El resultado

Probamos las trigonométricas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package funciones;
 
import ar.com.compujuy.funciones.Funcion;
import javax.script.ScriptException;
 
/**
 *
 * @author rafa22
 */
public class Funciones {
 
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws ScriptException{
      String cadena="sen(34*2)+tan x";
      Funcion f=new Funcion();
      f.evaluar(cadena, "1");
      System.out.println(f.getResultadoConversion());
      System.out.println(f.getResultadoOperacion());
    }
 
}

La salida

Probamos una mezcla de las anteriores

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package funciones;
 
import ar.com.compujuy.funciones.Funcion;
import javax.script.ScriptException;
 
/**
 *
 * @author rafa22
 */
public class Funciones {
 
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws ScriptException{
      String cadena="sen (34*x)+tan 1+32+x^(2+4-cos 3)";
      Funcion f=new Funcion();
      f.evaluar(cadena, "1.5");
      System.out.println(f.getResultadoConversion());
      System.out.println(f.getResultadoOperacion());
    }
 
}

El resultado

Nota : Si quiseramos probar con sen -2x o -2 ^x deberiamos poner sen (-2x) o (-2) ^x

Bien con esto terminamos, vimos la clase Funcion que nos puede resolver problemas que nos pueden aparecer como mencionamos en los métodos/analisis numéricos etc… hasta pronto…

Visto 1451 veces Modificado por última vez en Lunes, 01 Agosto 2016 18:39

Deja un comentario

Asegúrate de llenar la información requerida marcada con (*). No está permitido el código HTML. Tu dirección de correo NO será publicada.