Universidad de los Andes Informe proyecto diseño de algoritmos

Anuncio
Universidad de los Andes
Informe proyecto diseño de algoritmos
Grupo S1PY 3
David Arteaga., Código
Luis Páez., 200422567
Bogotá Diciembre 1 del 2011
Contenido
1 Algoritmo de solución
1.1 Explicación informal del algoritmo elegido.
1.2 Anotación (contexto, pre- y poscondición) métodos.
2 Análisis de complejidades espacial y temporal
2.1 Complejidad espacial
2.2 Complejidad temporal
3 Representación de datos
3.1Descripción de estructuras de información para guardar los datos de entrada.
4 Arquitectura de la solución java
4.1 Paquetes
4.2 Clases
4.3Ubicación del método main.
5 Comentarios finales
1 Algoritmo de solución
1.1 Explicación informal del algoritmo elegido.
El algoritmo de solución que utilizamos consiste en leer de un archivo en el cual se encuentra el nombre
de la ecuación, la ecuación a resolver en notación posfija, la incógnita que queremos despejar y el
número de pasos máximo para hacerlo.
Después de leer este archivo, tomamos la ecuación y la dividimos en sus partes, que son sus variables y
operaciones, y los guardamos en un arreglo de Strings con el cual después armamos un árbol binario
que representa la ecuación. El árbol de la ecuación se arma leyendo el arreglo de Strings de atrás hacia
adelante, quedando el nodo raíz con el elemento “=”, luego los elementos se van insertando como hijos
derechos consecutivamente a menos de que sean o una operación binaria, o una operación especial, en
cuyo caso se siguen las siguientes reglas:
1. Si es una operación binaria (“+”,“-”,“*”,“/”) se hace un nodo con elemento correspondiente a la
operación binaria leída en el arreglo, luego con los dos elementos siguientes a la operación
binaria en el arreglo se crean dos nodos con sus valores respectivos y estos se asignan como hijo
derecho e hijo izquierdo al nodo que contiene la operación.
2. Si es una operación especial (“2^”,“log”,“^2”,“rz”) se crea el nodo de la operación especial y se
le asigna solo un hijo al nodo, en este caso siempre se lo asignamos al hijo derecho del nodo con
la operación especial
Después de armar el árbol, tomamos la aproximación de apretar el cerco según si la ecuación es lineal,
cuadrática o si tiene potencias y logaritmos. Esto lo implementamos en el proyecto al evaluar la
ecuación que está en el árbol binario con diferentes métodos, lo que nos permite identificar qué tipo de
ecuación es. A continuación se describe brevemente como identificamos cada tipo de ecuación:

Verificación ecuación lineal
1. Se verifica que todas las apariciones de la incógnita a resolver en el árbol de la ecuación tengan
grado 1, es decir que la incógnita no se encuentre elevada al cuadrado
2. Se verifica que al aplicar tácticas de colección y atracción para la incógnita a resolver en el árbol
de la incógnita siga siendo de grado 1
3. Se verifica que en ningún caso algún nodo en el árbol de la ecuación que tenga las operaciones “*”
y “/” tengan como hijo izquierdo y derecho la incógnita a resolver.

Verificación ecuación cuadrática
1. Se verifica que el único operador especial que puede haber en el árbol en el árbol de la ecuación
sea el operador de elevar al cuadrado
2. Se verifica que el árbol de la ecuación se tenga el operador de elevar al cuadrado en un nodo y
como su hijo la incógnita a resolver
3. Se verifica si el árbol de la ecuación corresponde a una ecuación de la forma (x +- a)*(x +- b)=0

Verificación ecuación con operaciones especiales
1. Se busca en el árbol de la ecuación un nodo que contenga al menos un operador especial que
sea diferente al de elevar al cuadrado.
Cuando la ecuación es identificada satisfactoriamente, se empieza a transformar el árbol de la ecuación
para llegar a una solución. Para esto nosotros utilizamos algoritmos de agenda en cada caso que van
transformando el árbol según la incógnita que se quiera despejar. Como el número de nodos del árbol
binario que representa la ecuación en el árbol binario es finito se asegura que el proceso de solucionar
la ecuación debe terminar.
Para hallar una solución de una ecuación lineal se logra duplicando nodos y/o subárboles dependiendo
de la operación que tengan como elemento y se les cambia la operación que tienen por su opuesta
según las siguientes reglas:
Si se tiene un subárbol con elemento “+” y nodos hijos izquierdo y derecho se suplica el subárbol
cambiando el elemento “+” por “-”
Si se tiene un subárbol con elemento “-” y nodos hijos izquierdo y derecho se suplica el subárbol
cambiando el elemento “-” por “*”
Si se tiene un subárbol con elemento “*” y nodos hijos izquierdo y derecho se suplica el subárbol
cambiando el elemento “*” por “/”
Si se tiene un subárbol con elemento “/” y nodos hijos izquierdo y derecho se suplica el subárbol
cambiando el elemento “/” por “*”
Luego se añaden los nodos creados a los subárboles derecho e izquierdo de la raíz y aplicando tácticas
de colección y atracción se transforma el árbol de forma recursiva de tal forma que el nodo que tiene
como elemento a la incógnita a resolver quede de un lado del nodo raíz, y del otro un nodo o un árbol
que representa una expresión o un valor.
Para una ecuación cuadrática miramos que el árbol de la ecuación tenga la forma de una ecuación
igualada a 0(en nuestro caso el hijo derecho debe ser igual a 0), es decir uno de los nodos hijos de la raíz
con el elemento “=” es 0, y de allí ya podemos sacar los valores de los coeficientes de la ecuación para
resolverlo aplicando la formula cuadrática. En el caso en el que el árbol de la ecuación no cumpla con la
condición anteriormente mencionada se aplicaran tácticas de colección y atracción hasta llevar el árbol
de la ecuación a la forma igualada a 0.
1.2 Anotación (contexto, pre- y poscondición) métodos.
1. Método que mete la ecuación en un arreglo de strings y llama el método para armar el árbol
correspondiente a la ecuación y verifica la ecuación para tratar el árbol armado.
public static void resolverEcua(String ecuacion, int limite)
Ctx: ecuacion: ecuación en notación posfija, limite es entero
Pre: T
Pos: se parte el String ecuacion en sus elementos y se mete cada elemento en un arreglo de
strings
2. Método que arma el árbol binario correspondiente a la ecuación
public static void armarArbol(String[] laEcuacion)
Ctx: laEcuacion: array[0..n-1] of D
Pre: el arreglo de strings es correcto en notación posfija
Pos: se arma un árbol binario que representa la ecuación con los elementos del arreglo de
strings
3. Método que hace la recursión utilizada para armar el árbol correspondiente a la ecuación
public static void recursion(Nodo nod, String[] ecuacion, int pos )
Ctx: nod: Nodo es un nodo binario que representa el árbol de la ecuación ,
laEcuacion: array[0..n-1] of D, pos es entero
Pre: El nodo raíz tiene como elemento “=”
Pos: se encadenan los nodos para formar el árbol binario que representa la ecuación
4. método que realiza una búsqueda de un elemento en el árbol de una ecuación
public static boolean buscarElementoArbol(Nodo nod, String buscado)
Ctx: nod: Nodo es un nodo binario que representa el árbol de la ecuacion, String es un elemento
Pre: el árbol representado por el nodo es correcto
Pos: retorna un booleano true encontró el carácter buscado, de lo contrario retorna false
5. Recorrido en Postorden para imprimir la ecuación representada en el árbol
public static String imprimirArbol(Nodo nod)
Ctx: Nodo es un nodo binario que representa el árbol de la ecuación,
Pre: el árbol representado por el nodo es correcto
Pos: retorna un String con la ecuacion representada por el arbol
6. Método de "despeje" de "*"
public static void despejeMultDivSimple(Nodo nod)
Ctx: Nodo es un nodo binario que representa el árbol de la ecuación,
Pre: el árbol representado por el nodo es correcto
Pos: el árbol es transformado para dejar la incógnita sola como hijo derecho del nodo raíz
7. Método de "despeje" de "+" y "-"
public static void despejeMasMenosSimple(Nodo nod)
Ctx: Nodo es un nodo binario que representa el árbol de la ecuación,
Pre: el árbol representado por el nodo es correcto
Pos: el árbol es transformado para dejar la incógnita sola como hijo derecho del nodo raíz
8. Atracción simple R op R' = R'' donde op{+,-,*,/}
public static void atraccionSimple(Nodo nodo)
Ctx: Nodo es un nodo binario que representa el árbol de la ecuación,
Pre: el árbol representado por el nodo es correcto
Pos: el árbol es transformado para dejar la incógnita sola como hijo derecho del nodo raíz
9. Resuelve ecuaciones cuadráticas que sean iguales a "0"
public static double[] resolverCuadraticaSimple(int aa, int bb, int cc)
Ctx:aa,bb,cc son enteris
Pre:T
Pos: se retorna un arreglo de valores double que contiene el cálculo de las raíces de la incógnita
utilizando la formula cuadrática
10. Verifica si la ecuación es de tipo ax^2 + b*x + c = 0
public static boolean verificarCuadratica(Nodo nodo)
Ctx: Nodo es un nodo binario que representa el árbol de la ecuación,
Pre: el árbol representado por el nodo es correcto
Pos: retorna un booleano true si la verificación fue exitosa, de lo contrario retorna false
11. Método que verifica si un árbol que representa una ecuación cuadrática es correcto forma
(xopa)*(xopb) donde op{+,-}
public static boolean checkFormaCuadritica(Nodo elNodo)
Ctx: Nodo es un nodo binario que representa el árbol de la ecuación,
Pre: el árbol representado por el nodo es correcto
Pos: retorna un booleano true si la verificación fue exitosa, de lo contrario retorna false
12. Método que revisa si se puede "pasar" un más o menos que no involucre la incógnita; del lado
izquierdo al lado derecho del árbol
public static boolean checkSumaRestaSimple(Nodo nod)
Ctx: Nodo es un nodo binario que representa el árbol de la ecuación,
Pre: el árbol representado por el nodo es correcto
Pos: retorna un booleano true si la verificación fue exitosa, de lo contrario retorna false
13. Método que revisa si se puede pasar una multiplicación o una división puede involucre la
incógnita; del lado izquierdo al lado derecho del árbol
public static boolean checkMultDivisionSimple(Nodo nod)
Ctx: Nodo es un nodo binario que representa el árbol de la ecuación,
Pre: el árbol representado por el nodo es correcto
Pos: retorna un booleano true si la verificación fue exitosa, de lo contrario retorna false
14. Método que forma una ecuación cuadrática igualada a 0
public static void formarCuadratica(Nodo nod)
Ctx: Nodo es un nodo binario que representa el árbol de la ecuación,
Pre: el árbol representado por el nodo es correcto
Pos: retorna un booleano true si la verificación fue exitosa, de lo contrario retorna false
2 Análisis de complejidades espacial y temporal
2.1 Complejidad espacial
Cada ecuación que recibe el programa tiene un número n de elementos (los elementos son números,
letras u operadores)
En la solución propuesta se utilizan principalmente 2 estructuras de datos, que son un arreglo de Strings
y un árbol binario
Complejidad del arreglo de Strings: O(n)
Complejidad del árbol binario: O(log n)
2.2 Complejidad temporal
1.public static void resolverEcua(String ecuacion, int limite)
O(n log n)
2.public static void armarArbol(String[] laEcuacion)
O(log n)
3.public static void recursion(Nodo nod, String[] ecuacion, int pos )
O(log n)
4.public static boolean buscarElementoArbol(Nodo nod, String buscado)
O(log n)
5.public static String imprimirArbol(Nodo nod)
O(n)
6.public static void despejeMultDivSimple(Nodo nod)
O(log n)
7.public static void despejeMasMenosSimple(Nodo nod)
O(log n)
8.public static void atraccionSimple(Nodo nodo)
O(log n)
9.public static double[] resolverCuadraticaSimple(int aa, int bb, int cc)
O(1)
10.public static boolean verificarCuadratica(Nodo nodo)
O(1)
11.public static boolean checkFormaCuadritica(Nodo elNodo)
O(1)
12.public static boolean checkSumaRestaSimple(Nodo nod)
O(1)
13.public static boolean checkMultDivisionSimple(Nodo nod)
O(1)
14. public static void formarCuadratica(Nodo nod)
O(log n)
3 Representación de datos
En el proyecto la ecuación la obtenemos de un archivo externo que tiene la siguiente estructura:
Línea 1: nombre de la ecuación (ejemplo: “eca”)
Línea 2: ecuación en notación posfija (ejemplo: “a x 1 + log x 1 – log + a =”)
Línea 3: incógnita la cual se va a despejar (ejemplo “x”)
Línea 4: número máximo de pasos para resolver la ecuación (ejemplo “25”)
La ecuación luego de ser leída, es dividida en cada uno de sus elementos por el parser, y todos sus
elementos son guardados en un Arreglo de Strings, el cual luego será utilizado para armar un árbol
binario que representara la ecuación.
La estructura del árbol binario que se utilizó se basa en nodos, cuyo contenido es:
1. Un atributo String, que guarda 1 elemento de la ecuación
2. Un atributo Nodo, que modela el hijo derecho del nodo (si no tiene hijo entonces nodo=null)
3. Un atributo Nodo, que modela el hijo izquierdo del nodo (si no tiene hijo entonces nodo=null)
4 Arquitectura de la solución
4.1 Paquetes
La solución implementada tiene un solo paquete llamado mundo.
4.2 Clases
La solución implementada consta de 2 clases:
·
La clase Nodo, la cual modela un árbol binario
·
La clase mundo, la cual es la encargada de leer y hacer las validaciones y transformaciones
al árbol de la ecuación para encontrar la solución.
4.3 Ubicación del método main
El método main del el proyecto se encuentra en la clase mundo
5 Comentarios finales
Como alternativa de estructura de datos se podía haber utilizado una pila en vez de el árbol binario,
pero se escogió el árbol binario, ya que se podía modelar la ecuación que nos dan en un árbol sintáctico
y con este se tiene mucha más claridad de cómo se pueden hacer los chequeos y los cambios para
resolver la ecuación paso por paso.
Se intentó también utilizar el algoritmo de Shunting yard, pero para el enfoque que teníamos nos
resultaba muy complejo, así que decidimos implementar nuestro propio parser.
En la implementación de la solución hay que tener en cuenta muchas reglas de transición y realizar
muchos chequeos, y dada la cantidad de casos y reglas que se debían tener en cuenta no fue posible
implementar la solución para operaciones especiales.
El proyecto se puede extender muchas más funcionalidades de resolución de ecuaciones, como
derivadas e integrales y muchas más operaciones, pero como se menciona anteriormente se tendrían
que implementar nuevos chequeos y reglas por cada operación que se habilite.
Descargar