2009-06-18 26 views
11

Deseo desarrollar una aplicación GUI que muestre una ecuación matemática dada. Cuando hace clic sobre una variable particular en la ecuación para indicar que es la variable desconocida, es decir, que se calcula, la ecuación se transforma para evaluar la variable desconocida requerida.Manipulación de ecuaciones matemáticas en Python

Por ejemplo:


a = (b+c*d)/e 

Supongamos que hago clic en "d" para significar que es la variable desconocida. Entonces la ecuación debe ser re-estructurado para:

d = (a*e - b)/c 

A partir de ahora, sólo quiero saber cómo puedo ir sobre la reordenación de la ecuación dada en base a la entrada del usuario. Una sugerencia que obtuve de mi hermano fue usar representación notacional pre-fix/post-fix en back-end para evaluarlo.

¿Es ese el único camino o hay alguna sugerencia más simple? Además, utilizaré no solo funciones matemáticas básicas, sino también trignometría y cálculo (lo básico, creo, sin cálculo diferencial parcial y todo eso). Creo que la evaluación de notación pre/post-fix podría no ser útil en la evaluación de funciones matemáticas superiores.

Pero esa es solo mi opinión, así que por favor señale si estoy equivocado. Además, voy a utilizar SymPy para la evaluación matemática, por lo que la evaluación de una ecuación matemática dada no es un problema, la creación de una ecuación específica de un genérico dado es mi problema principal.

Respuesta

3

Sage tiene soporte para matemática simbólica. Se podía utilizar algunas de las funciones de manipulación de la ecuación incorporadas:

http://sagemath.org/

+0

En mi humilde opinión, es mucho mejor apuntar a SymPy aquí, en lugar de SAGE, que es un gran conglomerado de paquetes que ni siquiera se ejecuta en Windows (excepto en una máquina virtual, pero esto no cuenta) –

4

Lo que se quiere hacer no es fácil. Algunas ecuaciones son bastante sencillas para reorganizar (como b el sujeto de a = b*c+d, que es b = (a-d)/c), mientras que otras no son tan obvias (como x el sujeto de y = x*x + 4*x + 4), mientras que otras no son posibles (especialmente cuando las funciones trigonométricas y otras complicaciones).

Como han dicho otras personas, echa un vistazo a Sage. Se does lo que quiere:

You can solve equations for one variable in terms of others: 

sage: x, b, c = var('x b c') 
sage: solve([x^2 + b*x + c == 0],x) 
[x == -1/2*b - 1/2*sqrt(b^2 - 4*c), x == -1/2*b + 1/2*sqrt(b^2 - 4*c)] 
21

Usando SymPy, su ejemplo podría ser algo como esto:

>>> import sympy 
>>> a,b,c,d,e = sympy.symbols('abcde') 
>>> r = (b+c*d)/e 
>>> l = a 
>>> r = sympy.solve(l-r,d) 
>>> l = d 
>>> r 
[(-b + a*e)/c] 
>>> 

Parece que funciona para las funciones trigonométricas también:

>>> l = a 
>>> r = b*sympy.sin(c) 
>>> sympy.solve(l-r,c) 
[asin(a/b)] 
>>> 

Y ya que usted están trabajando con una GUI, (probablemente) querrá convertir de una cadena a otra expresiones:

>>> r = '(b+c*d)/e' 
>>> sympy.sympify(r) 
(b + c*d)/e 
>>> sympy.sstr(_) 
'(b + c*d)/e' 
>>> 

o puede que prefiera mostrarlos como representados LaTeX or MathML.

+1

+1 para dar posibles ejemplos útiles de SymPy en lugar de mostrar inmediatamente Sage (que, por cierto, incluye SymPy). –

6

Si desea hacer esto de la caja, sin depender de librairies, creo que los problemas que encontrará no están relacionados con Python. Si desea encontrar tales ecuaciones, debe describir las heurísticas necesarias para resolver estas ecuaciones.

Primero, debe representar su ecuación. ¿Qué hay de separación:

  • operandos:
    • operandos simbólicos (a, b)
    • operandos numéricos (1,2)
  • operadores:
    • operadores unarios (-, funciones trigonométricas)
    • operadores binarios (+, -, *, /)

operadores unarios, obviamente, adjunte un operando, operaciones binarias se encierran dos.

¿Qué pasa con los tipos?

Creo que todos estos componentes deberían derivarse de un único tipo común expression. Y esta clase tendría un método getsymbols para localizar rápidamente símbolos en sus expresiones.

Y entonces distinguir entre los operadores unarios y binarios, añadir unas complemento básico/reordenar primitivas ...

Algo así como:

class expression(object): 
    def symbols(self): 
     if not hasattr(self, '_symbols'): 
      self._symbols = self._getsymbols() 
     return self._symbols 
    def _getsymbols(self): 
     """ 
     return type: list of strings 
     """ 
     raise NotImplementedError 

class operand(expression): pass 

class symbolicoperand(operand): 
    def __init__(self, name): 
     self.name = name 
    def _getsymbols(self): 
     return [self.name] 
    def __str__(self): 
     return self.name 

class numericoperand(operand): 
    def __init__(self, value): 
     self.value = value 
    def _getsymbols(self): 
     return [] 
    def __str__(self): 
     return str(self.value) 

class operator(expression): pass 

class binaryoperator(operator): 
    def __init__(self, lop, rop): 
     """ 
     @type lop, rop: expression 
     """ 
     self.lop = lop 
     self.rop = rop 
    def _getsymbols(self): 
     return self.lop._getsymbols() + self.rop._getsymbols() 
    @staticmethod 
    def complementop(): 
     """ 
     Return complement operator: 
     op.complementop()(op(a,b), b) = a 
     """ 
     raise NotImplementedError 
    def reorder(): 
     """ 
     for op1(a,b) return op2(f(b),g(a)) such as op1(a,b) = op2(f(a),g(b)) 
     """ 
     raise NotImplementedError 
    def _getstr(self): 
     """ 
     string representing the operator alone 
     """ 
     raise NotImplementedError 
    def __str__(self): 
     lop = str(self.lop) 
     if isinstance(self.lop, operator): 
      lop = '(%s)' % lop 
     rop = str(self.rop) 
     if isinstance(self.rop, operator): 
      rop = '(%s)' % rop 
     return '%s%s%s' % (lop, self._getstr(), rop) 


class symetricoperator(binaryoperator): 
    def reorder(self): 
     return self.__class__(self.rop, self.lop) 

class asymetricoperator(binaryoperator): 
    @staticmethod 
    def _invert(operand): 
     """ 
     div._invert(a) -> 1/a 
     sub._invert(a) -> -a 
     """ 
     raise NotImplementedError 

    def reorder(self): 
     return self.complementop()(self._invert(self.rop), self.lop) 


class div(asymetricoperator): 
    @staticmethod 
    def _invert(operand): 
     if isinstance(operand, div): 
      return div(self.rop, self.lop) 
     else: 
      return div(numericoperand(1), operand) 
    @staticmethod 
    def complementop(): 
     return mul 
    def _getstr(self): 
     return '/' 

class mul(symetricoperator): 
    @staticmethod 
    def complementop(): 
     return div 
    def _getstr(self): 
     return '*' 

class add(symetricoperator): 
    @staticmethod 
    def complementop(): 
     return sub 
    def _getstr(self): 
     return '+' 

class sub(asymetricoperator): 
    @staticmethod 
    def _invert(operand): 
     if isinstance(operand, min): 
      return operand.op 
     else: 
      return min(operand) 
    @staticmethod 
    def complementop(): 
     return add 
    def _getstr(self): 
     return '-' 

class unaryoperator(operator): 
    def __init__(self, op): 
     """ 
     @type op: expression 
     """ 
     self.op = op 
    @staticmethod 
    def complement(expression): 
     raise NotImplementedError 

    def _getsymbols(self): 
     return self.op._getsymbols() 

class min(unaryoperator): 
    @staticmethod 
    def complement(expression): 
     if isinstance(expression, min): 
      return expression.op 
     else: 
      return min(expression) 
    def __str__(self): 
     return '-' + str(self.op) 

Con esta estructura básica establecido, debe ser capaz de describe una heurística simple para resolver ecuaciones muy simples. Solo piensa en las reglas simples que aprendiste para resolver ecuaciones y escríbelas. Eso debería funcionar :)

Y luego un solucionador muy ingenua:

def solve(left, right, symbol): 
    """ 
    @type left, right: expression 
    @type symbol: string 
    """ 
    if symbol not in left.symbols(): 
     if symbol not in right.symbols(): 
      raise ValueError('%s not in expressions' % symbol) 
     left, right = right, left 

    solved = False 
    while not solved: 
     if isinstance(left, operator): 
      if isinstance(left, unaryoperator): 
       complementor = left.complement 
       right = complementor(right) 
       left = complementor(left) 
      elif isinstance(left, binaryoperator): 
       if symbol in left.rop.symbols(): 
        left = left.reorder() 
       else: 
        right = left.complementop()(right, left.rop) 
        left = left.lop 
     elif isinstance(left, operand): 
      assert isinstance(left, symbolicoperand) 
      assert symbol==left.name 
      solved = True 

    print symbol,'=',right 

a,b,c,d,e = map(symbolicoperand, 'abcde') 

solve(a, div(add(b,mul(c,d)),e), 'd') # d = ((a*e)-b)/c 
solve(numericoperand(1), min(min(a)), 'a') # a = 1 
5

Las cosas han cambiado desde 2009. Seguro que no sé cómo va su aplicación con interfaz gráfica, pero esto ahora es posible directamente en IPython qtconsole (que se podría integrar dentro de una aplicación PyQt/PySide personalizado, y un seguimiento de todos los símbolos definidos, para permitir la interacción GUI en un cuadro de lista separada, etc.)

enter image description here

(Utiliza el sympyprt extensión para IPython)

Cuestiones relacionadas