16

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

例如:


a = (b+c*d)/e

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

d = (a*e - b)/c

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

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

不过这只是我个人的看法,如有错误请指出。此外,我将使用SymPy进行数学评估,因此对给定数学方程的评估不是问题,从给定的通用方程创建特定方程是我的主要问题。

4

5 回答 5

27

使用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 或 MathML

于 2009-06-18T03:40:25.023 回答
7

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

在此处输入图像描述

(使用sympyprtIPython 的扩展

于 2013-08-19T05:47:47.440 回答
7

如果你想开箱即用,不依​​赖库,我认为你会发现的问题与 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
于 2009-06-18T03:58:12.097 回答
4

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

正如其他人所说,请查看 Sage。它你想做的事:

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)]
于 2009-06-18T03:26:53.587 回答
2

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

http://sagemath.org/

于 2009-06-18T03:16:47.850 回答