简单的求导的符号运算算法

来源:互联网 发布:淘宝恶意刷流量软件 编辑:程序博客网 时间:2024/06/01 07:13

简单的求导的符号运算算法

  最近准备做一个有意思的事情,想实现基于符号运算法则来实现求导的算法。
  现在做出了一个比较简单的雏形,写一篇博客总结一下,顺便介绍一下这个python小项目。
  下面的思路和代码片段展现了我的思路,如果有感兴趣的同学应该可以写出实现,如果需要代码可以联系我。因为完整代码里面有很多无聊的提示信息和格式转化,所以我不打算放上来了。
  


表达式语法树

语法树

语法树是我这个项目里面所有表达式的存在形式,树的一个结点定义如下:

# the class of expression treeclass ExpTree:    def __init__(self, op, args):        self.op = op        self.args = args

前缀表达式转化为语法树

算法比较简单,递归实现表达式解析即可:

# cast a expression string in scheme formation to a expression treedef expr_to_tree(line):    # cast a expression element list in scheme formation to a expression tree    def expr_list_to_tree(line_list):        del line_list[0]        op = line_list[0]        del line_list[0]        args = []        while True:            if line_list[0] == ')':                del line_list[0]                return ExpTree(op, args)            if line_list[0] == '(':                args.append(expr_list_to_tree(line_list))            else:                args.append(line_list[0])                del line_list[0]    line = line.replace('(', '( ')    line = line.replace(')', ' )')    line_list = line.split()    return simplify(expr_list_to_tree(line_list))

对语法树求导

这里是算法的核心,这个版本只加入了二元运算的法则,一元函数的法则以后会加入。

算法为递归求导,规则如下:
  1.叶子结点:x导数为1,常数导数为0。
  2.非叶子结点,左右子树为l,r。那么结点的运算来递归求导:
  (l+r)=l+r
  (lr)=lr
  (lr)=lr+lr
  (l/r)=(lrlr)/(r2)
  (lr)=(rlr1)l(这个版本因为不支持一元函数ln,所以r必须为常数表达式)

实现如下:

# get the derivate tree of the derivate of the tree expressiondef derivate(tree):    if isinstance(tree, str):        if tree == 'x':            return '1'        else:            return '0'    op = tree.op    args = tree.args    if op in ['+', '-']:        a0 = args[0]        a1 = args[1]        return simplify(ExpTree(op, [derivate(a0), derivate(a1)]))    elif op == '*':        a0 = args[0]        a1 = args[1]        e1 = ExpTree('*', [derivate(a0), a1])        e2 = ExpTree('*', [a0, derivate(a1)])        return simplify(ExpTree('+', [e1, e2]))    elif op == '/':        a0 = args[0]        a1 = args[1]        nom = ExpTree('-', [ExpTree('*', [derivate(a0), a1]), ExpTree('*', [a0, derivate(a1)])])        denom = ExpTree('^', [a1, '2'])        return simplify(ExpTree('/', [nom, denom]))    elif op in ['**', '^']:        a0 = args[0]        a1 = args[1]        e1 = a1        e2 = ExpTree('^', [a0, ExpTree('-', [a1 ,'1'])])        return simplify(ExpTree('*', [ExpTree('*', [e1, e2]), derivate(a0)]))

表达式化简

因为上述的求导规则经常会产生0或者1项,例如:
(2x)=(2)x+2(x)=0x+21
所以需要化简,规则为:
  1.0+M=M+0=M
  2.M0=M
  3.1M=M1=M
  4.0M=M0=0
  5.M/1=M
  6.0/M=0
  
实现为:

# simplify a expression treedef simplify(tree):    # judge whether ele is a constant    def is_constant(ele):        if not isinstance(ele, str):            return False        return ele != 'x'    # judge whether ele is a constant equal to v    def equ_to_constant(ele, v):        eps = 1e-5        if not is_constant(ele):            return False        return abs(float(ele) - v) < eps    if isinstance(tree, str):        return tree    arg_num = len(tree.args)    for i in range(arg_num):        tree.args[i] = simplify(tree.args[i])    if tree.op == '+':        for i in [0, 1]:            if equ_to_constant(tree.args[i], 0):                tree = tree.args[1 - i]                return tree    elif tree.op == '-':        if equ_to_constant(tree.args[1], 0):            tree = tree.args[0]            return tree    elif tree.op == '*':        for i in [0, 1]:            if equ_to_constant(tree.args[i], 0):                tree = '0'                return tree            if equ_to_constant(tree.args[i], 1):                tree = tree.args[1 - i]                return tree    elif tree.op == '/':        if equ_to_constant(tree.args[0], 0):            tree = '0'            return tree        if equ_to_constant(tree.args[1], 1):            tree = tree.args[0]            return tree    for i in range(arg_num):        if not is_constant(tree.args[i]):            return tree    tree = str(calculate(tree, None))    return tree

从语法树生成函数

容易实现求值函数calculate(tree, v),可以递归计算树在x=v时候的值,那么我们怎么得到树对应的函数呢?
一个lambda表达式足矣: func = lambda x: calculate(tree, x)
注意这个高阶函数的用法。


演示

演示如下:

***zzy@zzy-lenovo-g50-80:~/zzy/SICP$ python3 derivate.py
This is a little interpreter for scheme formation function and its’ derivate:
Scheme formation of f(x) = 2 * x + 1 is (+ 1 (* 2 x)).
f(x) is the function f(x), f(2) is the value f(x)|(x=2).
f, f’, f” or f”’ is (high rank) derivates of f.
Please input f(x) in scheme formation or exit:
(* x (^ (+ x 2) 3))
Please input expression or exit:
f(x)
(* x (^ (+ x 2) 3))
(x * ((x + 2) ^ 3))
Please input expression or exit:
f’(x)
(+ (^ (+ x 2) 3) (* x (* 3 (^ (+ x 2) 2.0))))
(((x + 2) ^ 3) + (x * (3 * ((x + 2) ^ 2.0))))
Please input expression or exit:
f”(x)
(+ (* 3 (^ (+ x 2) 2.0)) (+ (* 3 (^ (+ x 2) 2.0)) (* x (* 3 (* 2.0 (^ (+ x 2) 1.0))))))
((3 * ((x + 2) ^ 2.0)) + ((3 * ((x + 2) ^ 2.0)) + (x * (3 * (2.0 * ((x + 2) ^ 1.0))))))
Please input expression or exit:
f(0)
0.0
Please input expression or exit:
f’(0)
8.0
Please input expression or exit:
exit
Please input f(x) in scheme formation or exit:
exit
Thank you!


下一阶段改进

下一阶段的改进目标是:
1. 增加对一元函数的支持,建立函数和导数表机制。
2. 增加中缀表达式输入转换机制,更符合自然语言。
3*. 使用交换,结合律和分配律,合并同类项,加强化简单功能。(不准备实现)

原创粉丝点击