首页 > 编程知识 正文

求导的符号运算规则,简单的求导运算公式

时间:2023-05-04 00:38:30 阅读:252522 作者:968

求导的符号运算

  这是最近做的一个python的小项目,内容是实现求导的符号运算,虽然我最终的实现化简能力能力比较弱,但是还是一个很有意思的项目。
  

语法树的建立 语法树的结构

  语法树是我这个项目里面所有表达式的存在形式。
  
  树的一个结点要么是字符串(常数或者变量),要么是内部结点,内部结点代表一棵子树,对应存储的结构体有两个字段:根存储的运算和子结点的列表,列表元素个数是操作符的元数,列表元素是子结点。
  
  例如前缀表达式(+ (* x 2) 1)表示2x+1,那么对应的树根结点是’+’,对应的子结点列表是[tree(2x), 1],其中tree(2x)又是一棵子语法树,根为’*’, 对应子结点的列表是[2, x]。即语法树是递归定义的,最终叶子结点是常量或者变量。

前缀表达式转化为语法树

  
  我的项目中约定输入前缀表达式,即类似lisp的表达式,所以转化非常方便。
  
  对于前缀表达式(op arg0, arg1, …, argn),先读入op为根结点操作符,然后读入参数列表即可建立语法树,注意读入参数列表过程中可能读到表达式,需要递归建树。

语法树的运算 求值

  
  利用前缀表达式的算法递归求值即可。遇到变量,代入具体数值即可。
  
  设calculate(tree,x)计算tree在变量取值x时候的值。

求函数

  
  因为要求一个函数而不是具体数值,所以需要返回一个函数或者lambda表达式,可以实现func(tree) 为lambda x: calculate(tree, x)。

求导

  这里是算法的核心。

  对于二元运算,算法为递归求导,规则如下:
  1.叶子结点:x导数为1,常数导数为0。
  2.非叶子结点,左右子树为l,r。那么结点的运算来递归求导:
   (l+r)′=l′+r′
   (l−r)′=l′−r′
   (l∗r)′=l∗r′+l′∗r
   (l/r)′=(l′∗r−l∗r′)/(r2)
   (lr)′=(rlr−1)∗l′+ln(l)lr∗r′
  手动实现这五条规则即可。

  对于一元函数,有统一的计算方法,即链式法则:
   h(g(x))′=h′(g(x))∗g′(x)
  所以需要保存一个函数表,保存下每一个函数的导函数,利用链式法则递归计算即可。注意我们这儿并不保存导函数的函数形式,而是保存如何建立导函数语法树的lambda表达式。、
  例如,ln(x)的导函数就是lambda x: ExpTree(‘’, [1, x])。

语法树化简

  
  因为上述的求导规则经常会产生0或者1项,例如:
   (2∗x)′=(2)′∗x+2∗(x)′=0∗x+2∗1
  
  所以需要化简,规则为:
  1. 0+M=M+0=M
  2. M−0=M
  3. 1∗M=M∗1=M
  4. 0∗M=M∗0=0
  5. M/1=M
  6. 0/M=0
  7. M1=M
  8. 0M=0
  手动实现规则即可。
  
  要注意的是,需要先化简子树来递归化简。另外,如果左右子树都是常数,可以直接计算。
  

界面和交互

  界面需要用户输入前缀表达式和运算指令,输出对应的函数或者函数值,另外输出对应函数的时候,为了直观,除了函数的中缀表达式,我还输出了函数图像。
  
  交互上我没有花和大力气,所以格式相对比较死板,不过提供了足够的帮助信息,应该用起来很方便。
  

还需要改进的地方

  但是,这毕竟只是一个耗时一天不到的小项目,要改进的地方还有很多:
  
  1.这个项目不能运用运算律和函数性质化简,所以以下这些化简,暂时无法完成:
  x-x=0,2x+3x=5x,sinx * sinx + cosx * cosx=1,ln(exp(x))=x。
  
  2.这个项目暂时不支持自己指定中间函数,这个涉及到词法语法分析等等,没有实现。

  3.对于重复表达式没有共享而是一一计算,没有优化。

  但是毕竟这是一个轻量级项目,所以不准备加入这些特性了。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。