【问题标题】:Mathematical equation manipulation in PythonPython中的数学方程操作
【发布时间】:2016-01-26 07:24:33
【问题描述】:

我想开发一个显示给定数学方程式的 GUI 应用程序。当您单击方程中的特定变量以表示它是未知变量(即要计算)时,方程会自行转换以评估所需的未知变量。

例如:


a = (b+c*d)/e

假设我点击“d”表示它是未知变量。那么方程应该被重构为:

d = (a*e - b)/c

到目前为止,我只想知道如何根据用户输入重新排列给定的方程。我从我哥哥那里得到的一个建议是在后端使用前缀/后缀符号表示来评估它。

这是唯一的方法还是有更简单的建议? 此外,我不仅会使用基本的数学函数,还会使用三角函数和微积分(我认为是基本的。没有偏微积分等等)。我认为 pre/post-fix 符号评估可能对评估更高的数学函数没有帮助。

这只是我的观点,如果我错了,请指出。 另外,我将使用SymPy 进行数学评估,因此对给定数学方程式的评估不是问题,从给定的通用方程式创建特定方程式是我的主要问题。

【问题讨论】:

    标签: python math equation


    【解决方案1】:

    使用SymPy,您的示例将如下所示:

    >>> 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]
    >>> 
    

    它似乎也适用于三角函数:

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

    而且由于您使用的是 GUI,因此您(可能)希望在字符串和表达式之间来回转换:

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

    或者您可能更喜欢将它们显示为呈现的LaTeX or MathML

    【讨论】:

    • +1 从 SymPy 本身中提供可能有用的示例,而不是立即提出 Sage(顺便提一下,其中包括 SymPy)。
    • 我认为第一个sn-p的第二行应该是a,b,c,d,e = sympy.symbols('a b c d e')
    【解决方案2】:

    如果你想开箱即用,不依​​赖库,我认为你会发现的问题与 Python 无关。如果你想找到这样的方程,你必须描述解这些方程所必需的启发式方法。

    首先,您必须表示您的方程式。分离呢:

    • 操作数:
      • 符号操作数 (a,b)
      • 数字操作数 (1,2)
    • 运营商:
      • 一元运算符(-、三角函数)
      • 二元运算符(+、-、*、/)

    一元运算符显然会包含一个操作数,二元运算符将包含两个。

    类型呢?

    我认为所有这些组件都应该派生自一个常见的expression 类型。 这个类将有一个getsymbols 方法来快速定位表达式中的符号。

    然后区分一元和二元运算符,添加一些基本的补码/重排序原语...

    类似:

    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)
    

    有了这个基本结构设置,您应该能够描述一个简单的启发式来解决非常简单的方程。想想你学到的解方程的简单规则,然后把它们写下来。这应该工作:)

    然后是一个非常幼稚的求解器:

    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
    

    【讨论】:

      【解决方案3】:

      自 2009 年以来情况确实发生了变化。我不知道您的 GUI 应用程序进展如何,但现在可以直接在 IPython qtconsole 中实现(可以嵌入到自定义 PyQt/PySide 应用程序中,并跟踪所有定义的符号,以允许在单独的列表框中进行 GUI 交互等)

      (使用 IPython 的 sympyprt 扩展

      【讨论】:

        【解决方案4】:

        你想做的事情并不容易。有些方程式很容易重新排列(例如将b 设为a = b*c+d 的主题,即b = (a-d)/c),而另一些则不那么明显(例如将x 设为y = x*x + 4*x + 4 的主题),而其他的是不可能的(尤其是当您使用三角函数和其他复杂情况时)。

        正如其他人所说,请查看 Sage。 does你想要什么:

        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)]
        

        【讨论】:

          【解决方案5】:

          Sage 支持符号数学。您可以只使用一些内置的方程操作函数:

          http://sagemath.org/

          【讨论】:

          • 恕我直言,最好在这里指向 SymPy,而不是 SAGE,后者是一个庞大的软件包集合体,甚至不能在 Windows 上运行(除了在虚拟机中,但这不算) .
          猜你喜欢
          • 1970-01-01
          • 2018-02-09
          • 2022-09-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-05
          相关资源
          最近更新 更多