白冥 发表于 2025-1-22 18:24:46

【Python】【原创】自定义符号运算模块及其使用案例

本帖最后由 白冥 于 2025-1-27 22:34 编辑

      本贴提供了一个自定义模块来处理数学单位、符号以及其组合运算,并能计算导数。它的设计目的是让用户能够使用面向对象的方式定义、组合、运算和求导数,适用于符号计算、数值计算和优化等场景。本贴将对该模块中的各个类、函数进行详细介绍,帮助用户理解如何使用该模块进行各种数学操作。


目录

1. 模块简介
2. 类定义
    ○ Unit类
    ○ _Constant类
    ○ Symbol类
    ○ 运算符类:_Add、_Sub、_Mul、_Div、_Pow
    ○ 函数类:_Exp、_Log、_Sin、_Cos
3. 函数定义    ○ exp函数    ○ log函数    ○ sin函数    ○ cos函数
4. 核心函数
    ○ derivative 函数
    ○ to_lambda 函数
    ○ Newton 函数
5. 示例代码
    ○ 简单数学运算
    ○ 求导数
    ○ 使用 Newton 求解方程
6. 常见问题与解决方案
一、模块介绍
    此模块的核心思想是通过面向对象的方式实现数学运算的符号表示。通过对数学表达式建模,使得可以对表达式进行各种运算、求导和数值化。特别适用于数学建模、符号计算、数值计算和求解优化问题等场景。

该模块支持如下操作:


[*]定义常量、符号、变量。
[*]支持加法、减法、乘法、除法、幂运算等数学运算符。
[*]支持导数计算。
[*]可以通过to_lambda将符号表达式转换为 Python 函数进行数值计算。
[*]提供了一个Newton函数来求解方程的数值解。


二、类定义
    2.1 Unit类
      Unit类是所有数学单位(符号、常量等)的基类。它定义了基本的运算符重载,如加法、减法、乘法、除法和幂运算,允许数学单位之间进行数学运算。同时,Unit类也定义了derivative方法和to_lambda方法,这两个方法分别用于求导和将符号表达式转化为 Python 可执行的函数。

import math
class Unit:
    def __new__(cls,*units):
      unit=super().__new__(cls)
      unit.units=units
      return unit
    def __add__(self,other):
      if isinstance(other,(int,float)):
            return _Add(self,_Constant(other))
      return _Add(self,other)
    def __sub__(self,other):
      if isinstance(other,(int,float)):
            return _Sub(self,_Constant(other))
      return _Sub(self,other)
    def __mul__(self,other):
      if isinstance(other,(int,float)):
            return _Mul(self,_Constant(other))
      return _Mul(self,other)
    def __truediv__(self,other):
      if isinstance(other,(int,float)):
            return _Div(self,_Constant(other))
      return _Div(self,other)
    def __pow__(self,other):
      if not isinstance(other,(int,float)):
            raise ValueError("Not a standard power function")
      return _Pow(self,_Constant(other))
    def derivative(self,var):
      return None
    def to_lambda(self,var):
      return lambda x: None
    def __getattr__(self, name):
      raise AttributeError(f"'{self.__class__.__name__}' class has no attribute '{name}'. Direct instantiation is not allowed.")

      __new__方法:
            ●用于初始化类的实例,接收若干个单位作为参数(例如,常量或符号)。
            ●不允许直接实例化Unit类, Unit类只能被子类继承。

      运算符重载:
            ●__add__ :实现加法。
            ●__sub__ :实现减法。
            ●__mul__ :实现乘法。
            ●__truediv__ :实现除法。
            ●__pow__ :实现幂运算。
      
      derivative和to_lambda:
            ●derivative:返回当前单位对指定变量的导数。
            ●to_lambda:返回一个Python Lambda函数,将符号表达式转化为数值计算的函数。

    2.2 _Constant类
      _Constant类表示常量。它继承自Unit类,并重写了derivative和to_lambda方法。

class _Constant(Unit):
    def __new__(cls,value):
      if cls is _Constant:
            raise TypeError("Cannot instantiate _Constant directly.")
      unit=super().__new__(cls)
      unit.value=value
      return unit
    def derivative(self,var):
      return _Constant(0)
    def to_lambda(self,var):
      return lambda x: self.value
    def __add__(self,other):
      if isinstance(other,(int,float)):
            return _Constant(self.value+other)
      return super().__add__(other)
    def __sub__(self,other):
      if isinstance(other,(int,float)):
            return _Constant(self.value-other)
      return super().__sub__(other)
    def __mul__(self,other):
      if isinstance(other,(int,float)):
            return _Constant(self.value*other)
      return super().__mul__(other)
    def __truediv__(self,other):
      if isinstance(other,(int,float)):
            return _Constant(self.value/other)
      return super().__truediv__(other)
    def __pow__(self,other):
      if not isinstance(other,(int,float)):
            raise ValueError("Not a standard power function")
      return _Constant(self.value**other)

      derivative方法:
            ●常数的导数是0,因此derivative方法返回一个常数0 。
      to_lambda方法:
            ●常数的Lambda函数就是一个恒定的数值,因此to_lambda返回一个返回常量值的 Lambda 函数。

    2.3 Symbol类
         Symbol类表示符号或变量。它继承自Unit类,表示数学表达式中的符号,如x、y等。

class Symbol(Unit):
    def __new__(cls,name):
      unit=super().__new__(cls)
      unit.name=name
      return unit
    def derivative(self,var):
      if self.name==var.name:
            return _Constant(1)
      return _Constant(0)
    def to_lambda(self,var):
      if self.name==var.name:
            return lambda x: x
      return lambda x: self

      derivative方法:
            ●Symbol对变量的导数遵循标准的求导法则:
                ◆如果符号是目标变量,则导数为 1。
                ◆否则,导数为 0。
      to_lambda方法:
            ●将符号转化为 Lambda 函数,表示一个以x为输入的变量函数。

    2.4 运算符类:_Add、_Sub、_Mul、_Div、_Pow
      这些类分别实现了加法、减法、乘法、除法和幂运算。这些类的设计方式与Unit类相似,并实现了与运算符相关的derivative和to_lambda方法。

class _Add(Unit):
    def __new__(cls,unit_0,unit_1):
      if cls is _Add:
            raise TypeError("Cannot instantiate _Add directly.")
      return super().__new__(cls,unit_0,unit_1)
    def derivative(self,var):
      u,v=self.units
      der_u=u.derivative(var)
      der_v=v.derivative(var)
      return der_u+der_v
    def to_lambda(self,var):
      u,v=self.units
      f=u.to_lambda(var)
      g=v.to_lambda(var)
      return lambda x: f(x)+g(x)
class _Sub(Unit):
    def __new__(cls,unit_0,unit_1):
      if cls is _Sub:
            raise TypeError("Cannot instantiate _Sub directly.")
      return super().__new__(cls,unit_0,unit_1)
    def derivative(self,var):
      u,v=self.units
      der_u=u.derivative(var)
      der_v=v.derivative(var)
      return der_u-der_v
    def to_lambda(self,var):
      u,v=self.units
      f=u.to_lambda(var)
      g=v.to_lambda(var)
      return lambda x: f(x)-g(x)
class _Mul(Unit):
    def __new__(cls,unit_0,unit_1):
      if cls is _Mul:
            raise TypeError("Cannot instantiate _Mul directly.")
      return super().__new__(cls,unit_0,unit_1)
    def derivative(self,var):
      u,v=self.units
      der_u=u.derivative(var)
      der_v=v.derivative(var)
      return der_u*v+u*der_v
    def to_lambda(self,var):
      u,v=self.units
      f=u.to_lambda(var)
      g=v.to_lambda(var)
      return lambda x: f(x)*g(x)
class _Div(Unit):
    def __new__(cls,unit_0,unit_1):
      if cls is _Div:
            raise TypeError("Cannot instantiate _Div directly.")
      return super().__new__(cls,unit_0,unit_1)
    def derivative(self,var):
      u,v=self.units
      der_u=u.derivative(var)
      der_v=v.derivative(var)
      return (der_u*v-u*der_v)/v**_Constant(2)
    def to_lambda(self,var):
      u,v=self.units
      f=u.to_lambda(var)
      g=v.to_lambda(var)
      return lambda x: f(x)/g(x)
class _Pow(Unit):
    def __new__(cls,base,exp):
      if cls is _Pow:
            raise TypeError("Cannot instantiate _Pow directly.")
      return super().__new__(cls,base,exp)
    def derivative(self, var):
      u, a = self.units
      der_u=u.derivative(var)
      return a*(u**(a-1))*der_u
    def to_lambda(self,var):
      u, a = self.units
      f=u.to_lambda(var)
      return lambda x: f(x)**a.value

      derivative方法:
            ●对于加法、减法和乘法,采用导数的基本法则,分别进行逐项求导。
            ●对于除法,使用商法则。
            ●对于幂运算,使用幂函数求导公式。
      to_lambda方法:
            ●将运算表达式转化为 Lambda 函数,依次将其组成的两个单位转化为 Lambda 函数,然后进行对应的运算。

    2.5 函数类:_Exp、_Log、_Sin、_Cos
      这些类分别实现了指数函数、对数函数、正弦函数和余弦函数。

class _Exp(Unit):
    def __new__(cls,base,exp):
      if cls is _Exp:
            raise TypeError("Cannot instantiate _Exp directly.")
      return super().__new__(cls,base,exp)
    def derivative(self, var):
      a,u=self.units
      der_u=u.derivative(var)
      return (a**u)*log(_Constant(math.e),a)*der_u
    def to_lambda(self,var):
      a,u = self.units
      f=u.to_lambda(var)
      return lambda x: a.value**f(x)
class _Log(Unit):
    def __new__(cls,base,antilog):
      if cls is _Log:
            raise TypeError("Cannot instantiate _Log directly.")
      return super().__new__(cls,base,antilog)
    def derivative(self, var):
      a,u=self.units
      der_u=u.derivative(var)
      return der_u/(u*log(_Constant(math.e),a))
    def to_lambda(self,var):
      a,u = self.units
      f=u.to_lambda(var)
      return lambda x: math.log(f(x),a.value)
class _Sin(Unit):
    def __new__(cls,radian):
      if cls is _Sin:
            raise TypeError("Cannot instantiate _Sin directly.")
      return super().__new__(cls,radian)
    def derivative(self, var):
      u = self.units
      der_u=u.derivative(var)
      return cos(u)*der_u
    def to_lambda(self,var):
      u = self.units
      f=u.to_lambda(var)
      return lambda x: math.sin(f(x))
class _Cos(Unit):
    def __new__(cls,radian):
      if cls is _Cos:
            raise TypeError("Cannot instantiate _Cos directly.")
      return super().__new__(cls,radian)
    def derivative(self, var):
      u = self.units
            der_u=u.derivative(var)
      return -sin(u)*der_u
    def to_lambda(self,var):
      u = self.units
      f=u.to_lambda(var)
      return lambda x: math.cos(f(x))

      derivative方法:
            ●对于指数函数,使用链式法则。
            ●对于对数函数,使用对数求导公式。
            ●对于正弦和余弦函数,使用标准的三角函数求导公式。
      to_lambda方法:
            ●依照数学函数的定义,返回相应的 Lambda 函数。

三、函数定义:exp,log,sin,cos函数
      这些是用于构建特定数学函数的便捷函数

def exp(base,exp):
    if not isinstance(base,(int,float)):
      raise ValueError("Not a standard exponential function")
    if isinstance(exp,(int,float)):
      return _Constant(base**exp)
    return _Exp(_Constant(base),exp)
def log(base,antilog):
    if not isinstance(base,(int,float)):
      raise ValueError("Not a standard logarithmic function")
    if isinstance(antilog,(int,float)):
      return _Constant(math.log(antilog,base))
    if base<0:
      raise ValueError("Undefined")
    if base==1:
      raise ValueError("Meaningless")
    return _Log(_Constant(base),antilog)
def sin(radian):
    if isinstance(radian,(int,float)):
      return _Constant(math.sin(radian))
    return _Sin(radian)
def cos(radian):
    if isinstance(radian,(int,float)):
      return _Constant(math.cos(radian))
    return _Cos(radian)

    exp(base, exp):表示 base 的 exp 次幂。
    log(base, antilog):表示以 base 为底,antilog 为真数的对数。
    sin(radian):表示 radian 的正弦值。
    cos(radian):表示 radian 的余弦值。

四、核心函数
    4.1 derivative函数
      该函数用于计算数学表达式的导数。它接受两个参数:
            ●expr:一个表示数学表达式的Unit对象。
            ●var:一个表示变量的Symbol对象。
    4.2 to_lambda函数
      该函数用于将数学表达式转化为Lambda函数。返回的Lambda函数可以接受一个数值输入,并返回相应的计算结果。
    4.3 Newton函数
      该函数实现了牛顿法(Newton's method)用于求解方程的根。输入:
            ●x_0 :初始猜测值。
            ●expr :一个表示方程的数学表达式。
            ●var :方程中的变量。
            ●tol :精度容忍度。
            ●max_time :最大迭代次数。
      返回方程的数值解,或者如果在最大迭代次数内无法收敛,则返回None 。
def derivative(expr,var):
    return expr.derivative(var)
def to_lambda(expr,var):
    return expr.to_lambda(var)
def Newton(x_0,expr,var,tol,max_time):
    x_i=x_0
    f=to_lambda(expr,var)
    der_f=to_lambda(derivative(expr,var))
    for _ in range(max_time):
      x_i_1=x_i-f(x_i)/der_f(x_i)
      if abs(x_i_1-x_i)<tol:
            return x_i_1
      x_i=x_i_1
    return None

五、示例代码:
    5.1 简单数学运算

# 定义符号
x = Symbol('x')

# 定义表达式
expr = x ** 2 + 5 * x + exp(2, x)

# 生成lambda函数
f = to_lambda(expr, x)

# 输出函数值
print(f(1))# 输出:8
    5.2 求导函数

# 定义符号
x = Symbol('x')

# 定义表达式
expr = sin(x) * cos(x)

# 求导
deriv = derivative(expr, x)

# 输出导数
print(deriv)# 输出:cos(x)**2 - sin(x)**2
    5.3 使用Newton方法求解方程

# 定义符号
x = Symbol('x')

# 定义方程 f(x) = x^2 - 2
expr = x**2 - _Constant(2)

# 使用牛顿法求解
root = Newton(1.0, expr, x, 1e-6, 100)

# 输出结果
print(root)# 输出:1.414213562373095

六、常见问题与解决方案

    问题 1:如何定义新的数学单位?
      用户可以通过继承Unit类创建新的数学单位,并实现必要的方法如derivative和to_lambda 。
    问题 2:为什么我的运算结果不符合预期?
      确保符号和常量的使用方法正确,并检查导数的计算规则是否适用。
    问题 3:如何处理无法收敛的方程?
      在使用Newton方法时,可以增加max_time参数或调整初始猜测值来提高收敛性。

2302594 发表于 2025-1-31 07:42:51

头好痒,感觉要长出脑子了.jpg

芋头先森 发表于 2025-1-28 15:09:20

嗯?这是哪个论坛?好吧,我以为我点进了博客园;P

Sam30 发表于 2025-1-27 16:52:59

感覺是比較硬核的內容捏, 不過大概也是有友友可以用上的捏:loveliness:

cjamno 发表于 2025-1-25 11:59:22

额,在这里发Python合适吗,而且这个太基础了吧,感觉用c会更顺眼点

yota 发表于 2025-1-25 07:57:05

好硬合的内容,我在泥潭学技术.jpg,先码住了

zhuovboyan 发表于 2025-1-23 18:58:44

QAQ 刚从隔壁帖子出来 今天是第二次长脑子了jpg

威风妖怪麒麟 发表于 2025-1-23 01:05:37

自己重载运算符的时候,感觉还是很容易出现边界或者特殊值的问题的

PURO_ 发表于 2025-1-22 20:28:28

泥潭尊素卧虎藏龙,代码大佬满天飞惹{:6_190:}

SweetUncle 发表于 2025-1-22 20:09:17

https://img.gamemale.com/album/202408/03/102121ubl2b4leibsmzs7s.gif别卷了别卷了,你这是要拿ACM金奖吗

娱乐法师火布偶 发表于 2025-1-22 19:24:21

本质上应该还是C++里面的重载,不过python可能写起来更简单

phillipé 发表于 2025-1-22 19:07:14

介素在?真不想放假了在家还天天学这些惹,饶了我吧,我放假只想搞瑟瑟:'(
页: [1]
查看完整版本: 【Python】【原创】自定义符号运算模块及其使用案例