在Python中使用模糊三角数计算符号表达式

哈Ha! 今天是一个微型教程,内容涉及如何使用数学表达式解析字符串并使用模糊三角数计算字符串。 适当更改代码后,本教程将与其他“自定义”变量一起使用。 帮助:模糊三角数-模糊数的特殊情况(数字轴上的模糊变量)。 我建议在这里这里更详细地结识。

要求:

  • 编程语言python 3.x (本文中提供的代码已在python 3.5上进行了测试)
  • sympy,可以通过终端(控制台)安装:

    pip install sympy 

解决问题的过程:

  1. 我们连接图书馆

     from fractions import Fraction import re from typing import Iterable from random import random import sympy 

    连接分数是可选的,我们将使用分数以分数形式存储实数(以最大程度地减少精度损失)。 我们将使用re库来解析字符串并自动生成字符变量列表。

    使用类型库是可选的;我们使用它来明确指示函数参数的类型。 随机库将用于生成模糊变量的测试值。 sympy是一个很棒的Python字符计算库,我们将使用它来处理表达式字符串本身。
  2. 我们描述了模糊三角数的类及其上的运算。 在此示例中,三个操作就足够了(加,减和除法)。 我们将使用相应类的“魔术”方法的重载来介绍操作:

     class FuzzyTriangular(object): """  FuzzyTriangular""" def __init__(self, floatdigit = None, ABC = None, CAB = None, CDD = None): super(FuzzyTriangular, self).__init__() if ABC or floatdigit: if isinstance(floatdigit, (int, float)): self._a = Fraction(floatdigit) # "0" self._b = Fraction(floatdigit) # ("1") self._c = Fraction(floatdigit) # "0" elif isinstance(floatdigit, (tuple,list)): if len(floatdigit) == 2: #    self._a = Fraction(floatdigit[0] - abs(floatdigit[1])) # "0" self._b = Fraction(floatdigit[0]) # ("1") self._c = Fraction(floatdigit[0] + abs(floatdigit[1])) # "0" else: #3  ,   3 self._a = Fraction(floatdigit[0]) # "0" self._b = Fraction(floatdigit[1]) # ("1") self._c = Fraction(floatdigit[2]) # "0" else: self._a = Fraction(ABC[0]) # "0" self._b = Fraction(ABC[1]) # ("1") self._c = Fraction(ABC[2]) # "0" self._center = self._b # self._alpha = self._b - self._a #    self._beta = self._c - self._b #    self._d = (self._alpha + self._beta)/2 self._delta = (self._beta - self._alpha)/2 elif CAB: self._center = Fraction(CAB[0]) # self._alpha = Fraction(CAB[1]) #    self._beta = Fraction(CAB[2]) #    self._d = (self._alpha + self._beta)/2 self._delta = (self._beta - self._alpha)/2 self._b = self._center # ("1") self._a = self._center - self._alpha # "0" self._c = self._center + self._beta # "0" elif CDD: self._center = Fraction(CDD[0]) # self._d = Fraction(CDD[1]) self._delta = Fraction(CDD[2]) self._alpha = self._d - self._delta #    self._beta = self._d + self._delta #    self._b = self._center # ("1") self._a = self._center - self._alpha # "0" self._c = self._center + self._beta # "0" else: raise Exception("No input data to create class") def __repr__(self): return str((round(float(self._a), 12), round(float(self._b), 12),\ round(float(self._c), 12))) def __CDD_add(self, other): center = self._center + other._center d = self._d + other._d delta = self._delta + other._delta return FuzzyTriangular(CDD = (center, d, delta)) def __CDD_sub(self, other): center = self._center - other._center d = self._d + other._d delta = self._delta - other._delta return FuzzyTriangular(CDD = (center, d, delta)) def __CDD_mul(self, other): center = self._center*other._center d = abs(self._center)*other._d + abs(other._center)*self._d delta = self._center*other._delta + other._center*self._delta return FuzzyTriangular(CDD = (center, d, delta)) def __add__(self, other): if isinstance(other, FuzzyTriangular): return self.__CDD_add(other) else: return self.__CDD_add(FuzzyTriangular(other)) def __sub__(self, other): if isinstance(other, FuzzyTriangular): return self.__CDD_sub(other) else: return self.__CDD_sub(FuzzyTriangular(other)) def __mul__(self,other): if isinstance(other, FuzzyTriangular): return self.__CDD_mul(other) else: return self.__CDD_mul(FuzzyTriangular(other)) def __pos__(self): return FuzzyTriangular(1)*self def __neg__(self): return FuzzyTriangular(-1)*self def __eq__(self, other): return (self._a == other._a) and (self._b == other._b) and \ (self._c == other._c) 

    模糊三角数的表示形式可以不同,我们将不再赘述。 在给出的代码中,我们将注意方法__add__(加法运算符),__sub__(减法运算符),__mul__(乘法运算符)。 如果尝试将实数添加到模糊三角数,它将被转换为模糊三角数。 具有元组或实数列表的类似情况-前三个数将被视为模糊三角形(并转换为FuzzyTriangular类)。 __pos__方法将覆盖一元运算符“ +”。 __neg__方法是一元“-”。 __eq__方法将覆盖==运算符。 如果需要,还可以重新定义操作,例如:

    • 求幂
    • 数模
    • 比较(更多/更少,更多或相等/更少或相等)
    • 标量化(转换为int,float,复数,舍入)
    • 反演和其他...

    您可以使用一小组测试来检查输入的操作是否足够,例如:

     ZERO = FuzzyTriangular((0,0,0)) ONE = FuzzyTriangular((1,1,1)) A = FuzzyTriangular((0.3,0.5,0.9)) B = FuzzyTriangular((0.2,0.4,0.67)) C = FuzzyTriangular((0,0.33,0.72)) print('ZERO = '+str(ZERO)) print('ONE = '+str(ONE)) print('A = '+str(A)) print('B = '+str(B)) print('C = '+str(C)) #some tests print('\n') print('A + B = ', A + B) print('A + B == B + A', A + B == B + A) #   print('A + C = ', A + C) print('A + C == C + A', A + C == C + A) print('B + C = ', B + C) print('B + C == C + B', B + C == C + B) print('A + B + C = ', A + B + C) print('(A + B) + C == A + (B + C) == (A + C) + B', \ (A + B) + C == A + (B + C) == (A + C) + B) print('C + 1 = ', C + 1) print('1 + C = ', ONE + C) print('\n') print('A - A =', A - A) print('A - A == 0', A - A == ZERO) print('A - B = ', A - B) print('B - A = ', B - A) #   "-"  "+" print('A - B == -(B - A)', A - B == -(B - A)) print('(A + B + C) - (A + B) = ', (A + B + C) - (A + B)) #    print('(A + B + C) - (A + B) == C', (A + B + C) - (A + B) == C) print('1 - A = ', ONE - A) print('A - 1 = ', A - 1) print('1 - A == -(A - 1)', ONE - A == -(A - 1)) print('\n') print('A*B == B*A', A*B == B*A) print('-1*C =', -ONE*C) print('-1*C == -C', -ONE*C == -C) print('-1*C == C*-1', -ONE*C == C*-1) print('C*-1 = ', C*-1) print('C*-1 =', C*-1) print('-C*1 == -C', -C*1 == -C) print('-C*1 =', -C*1) print('-C =', -C) print('C*-1 == -C', C*-1 == -C) print('(A + B)*C == A*C + B*C', (A + B)*C == A*C + B*C) print('(A - B)*C == A*C - B*C', (A - B)*C == A*C - B*C) print('A*C = ', A*C) print('B*C = ', B*C) print('-B*C = ', -B*C) print('-B*C == B*-C', -B*C == B*-C) print('B*C == -B*-C', B*C == -B*-C) 

    这些加法,除法和乘法的验证操作在代码中指定,并根据“魔术”方法的重新定义进行。 我们希望能够使用以前未知的表达式中的符号变量来执行相同的操作。 这需要引入一些辅助功能。
  3. 我们介绍辅助功能:

    •  def symbols_from_expr(expr_str: str, pattern=r"[A-Za-z]\d{,2}") -> tuple: """       """ symbols_set = set(re.findall(pattern, expr_str)) symbols_set = sorted(symbols_set) symbols_list = tuple(sympy.symbols(symbols_set)) return symbols_list 
      我们将使用此函数在表达式字符串中搜索字符变量(默认模板是从A到Z或从a到z的字符,后跟一个整数,最长2个字符(或没有数字)。
    •  def expr_subs(expr_str: str, symbols: Iterable, values: Iterable): """    values   symbols  - expr_str""" expr = sympy.sympify(expr_str) func = sympy.lambdify(tuple(symbols), expr, 'sympy') return func(*values) 

      此函数允许您使用替换而不是任何有效类型的符号变量变量来计算字符串表达式的值(如果字符串表达式本身包含的操作被覆盖)。 这要归功于sympy.lambdify函数,该函数将sympy表达式转换为接受“魔术”方法的lambda函数。 函数正常运行的重要条件是符号和值中元素的正确顺序(符号和替换值的对应关系)。
    • 每次创建lambda函数都是昂贵的。 如果需要多次使用相同的表达式,建议使用以下两个函数:

       def lambda_func(expr_str: str, symbols: Iterable) -> callable: """ -,    - expr_str   symbols""" expr = sympy.sympify(expr_str) func = sympy.lambdify(tuple(symbols), expr, 'sympy') return func def func_subs(expr_func: callable, values: Iterable): """   - expr_func   values""" return expr_func(*values) 

      第一个返回lambda函数本身,而第二个则允许您通过替换值列表来计算结果值。 再一次,注意力集中在所使用的值不必是三角模糊数这一事实上。

  4. 我们从文件中读取公式行

     with open('expr.txt', 'r') as file: expr_str = file.read() print('expr_str', expr_str) 

    这样的东西可以用作expr.txt文件的行公式:

     p36*q67*p57*p26*p25*p13*q12*q15 + + p36*q67*p47*p26*p24*p13*q12 + + p67*q57*p26*p25*q12*p15 + + q57*p47*p25*p24*q12*p15 + + p57*p25*p12*q15 + + p36*p67*p13 + + p67*p26*p12 + + p47*p24*p12 + + p57*p15 - - p57*p47*p24*p12*p15 - - p67*p47*p26*p24*p12 - - p67*p57*p26*p12*p15 + + p67*p57*p47*p26*p24*p12*p15 - - p36*p67*p26*p13*p12 - - p36*p67*p47*p24*p13*p12 - - p36*p67*p57*p13*p15 + + p36*p67*p57*p47*p24*p13*p12*p15 + + p36*p67*p47*p26*p24*p13*p12 + + p36*p67*p57*p26*p13*p12*p15 - - p36*p67*p57*p47*p26*p24*p13*p12*p15 - - p36*p67*p57*p25*p13*p12*q15 - - p67*p57*p26*p25*p12*q15 - - p57*p47*p25*p24*p12*q15 + + p67*p57*p47*p26*p25*p24*p12*q15 + + p36*p67*p57*p26*p25*p13*p12*q15 + + p36*p67*p57*p47*p25*p24*p13*p12*q15 - - p36*p67*p57*p47*p26*p25*p24*p13*p12*q15 - - p36*p67*q57*p47*q26*p25*p24*p13*q12*p15 - - p67*q57*p47*p26*p25*p24*q12*p15 - - p36*p67*q57*p26*p25*p13*q12*p15 - - p36*q67*q57*p47*p26*p25*p24*p13*q12*p15 - - p36*q67*p57*p47*p26*p24*p13*q12*p15 - - p36*q67*p57*p47*p26*p25*p24*p13*q12*q15 
  5. 我们从字符串表达式中获取字符变量:

     symbols = symbols_from_expr(expr_str) print('AutoSymbols', symbols) 
  6. 我们生成测试随机三角数:

     values = tuple([FuzzyTriangular(sorted([random(),random(),random()]))\ for i in range(len(symbols))]) 

    需要对随机值进行排序,以匹配左侧“ 0”,中间和右侧“ 0”的值的顺序。
  7. 将公式字符串转换为表达式:

     func = lambda_func(expr_str, symbols) print('func', '=', func) 
  8. 我们使用lambda函数计算公式的值(我们使用func_subs和expr_subs来确保结果匹配):

     print('func_subs', '=', func_subs(func, values)) print('expr_subs', '=', expr_subs(expr_str, symbols, values)) 

输出示例:

 expr_str p36*q67*p57*p26*p25*p13*q12*q15 + + p36*q67*p47*p26*p24*p13*q12 + + p67*q57*p26*p25*q12*p15 + + q57*p47*p25*p24*q12*p15 + + p57*p25*p12*q15 + + p36*p67*p13 + + p67*p26*p12 + + p47*p24*p12 + + p57*p15 - - p57*p47*p24*p12*p15 - - p67*p47*p26*p24*p12 - - p67*p57*p26*p12*p15 + + p67*p57*p47*p26*p24*p12*p15 - - p36*p67*p26*p13*p12 - - p36*p67*p47*p24*p13*p12 - - p36*p67*p57*p13*p15 + + p36*p67*p57*p47*p24*p13*p12*p15 + + p36*p67*p47*p26*p24*p13*p12 + + p36*p67*p57*p26*p13*p12*p15 - - p36*p67*p57*p47*p26*p24*p13*p12*p15 - - p36*p67*p57*p25*p13*p12*q15 - - p67*p57*p26*p25*p12*q15 - - p57*p47*p25*p24*p12*q15 + + p67*p57*p47*p26*p25*p24*p12*q15 + + p36*p67*p57*p26*p25*p13*p12*q15 + + p36*p67*p57*p47*p25*p24*p13*p12*q15 - - p36*p67*p57*p47*p26*p25*p24*p13*p12*q15 - - p36*p67*q57*p47*q26*p25*p24*p13*q12*p15 - - p67*q57*p47*p26*p25*p24*q12*p15 - - p36*p67*q57*p26*p25*p13*q12*p15 - - p36*q67*q57*p47*p26*p25*p24*p13*q12*p15 - - p36*q67*p57*p47*p26*p24*p13*q12*p15 - - p36*q67*p57*p47*p26*p25*p24*p13*q12*q15 AutoSymbols (p12, p13, p15, p24, p25, p26, p36, p47, p57, p67, q12, q15, q26, q57, q67) func = <function <lambda> at 0x06129C00> func_subs = (-0.391482058715, 0.812813114469, 2.409570627378) expr_subs = (-0.391482058715, 0.812813114469, 2.409570627378) [Finished in 1.5s] 

本教程已结束。 希望您在这里找到有用的东西!

PS:所描述方法的主要“功能”是超越python和sympy的标准类型的变量和对其进行操作的能力。 通过声明您的类并重载“魔术”方法,您可以使用sympy(创建接受标准和用户类型及操作的lambda函数)来计算以前未知的数学表达式。

感谢您的关注!

Source: https://habr.com/ru/post/zh-CN443104/


All Articles