首页 > 其他分享 >一次符号计算的尝试:基于Common Lisp的微分符号计算实现

一次符号计算的尝试:基于Common Lisp的微分符号计算实现

时间:2023-10-03 15:44:21浏览次数:44  
标签:non right Lisp expr Common 符号计算 表达式 arguments

绪论

背景

作为一门具有极强表达能力的语言,Common Lisp适合于编译器实现、符号计算等应用。符号计算对于自动做题机器等方面具有广泛的应用。由于Common Lisp代码本身即为定义良好的抽象语法树(AST),因此对于实现编译器、符号计算具有天然的优势。本文基于语义分析器(Sematic Analyzer),以微分运算为例,对符号运算作了一定的探讨,证明了编译原理用于符号计算程序的编写的可行性。

本文的主要内容与意义

本文得出用于符号计算的语义分析器抽象。基于语义分析器,以关于x的四则运算一元表达式以及微分运算为例,对符号运算作了一定的探讨。基于语义分析器,实现了表达式的一种简易的简化功能。并对于一具体的四则运算一元表达式验证了所实现的函数功能。证明了编译原理用于符号计算程序的编写的可行性。

语义分析器抽象

根据微分运算的一个早先版本抽象出一个语义分析器。

(defmacro sematic (expr manage-atom &rest op-list)
  "语义分析器抽象,用于符号计算"
  `(labels
       ((computation (,expr)
          (let ((expr ,expr))
            (if (atom expr)
                (funcall ,manage-atom expr) ; manage-atom: 原子处理方法
                (let* ((operator-name (car expr))
                       (arguments (cdr expr))
                       (evaluated-arguments (mapcar #'computation arguments)))
                  (case operator-name
                    ,@op-list ; op-list: 运算符列表
                    (t (error "Not an allowed operator. "))))))))
     (computation ,expr)))

该语义分析器以递归方式定义,其作用为自最内向外逐步对S表达式进行处理,从而完成对整个AST的处理。在本文中,用于符号计算的语义分析器的作用是根据给定的AST,基于一定的规则生成新的AST。

语义分析器有三个输入参数,分别为待处理表达式,原子(atom)处理方法,以及运算符列表。

  • 待处理表达式:为S表达式,天然为定义良好的AST。由于Common Lisp本身的语法特性,可以直接跳过词法分析、语法分析阶段,直接处理AST,而无需预先生成
  • 原子处理方法:Common Lisp中,原子为非列表的表达式,如数字、符号、字符串等。
  • 运算符列表:为Common Lisp中case语句的一部分。用于定义对于各运算符(+、-、×、÷),分别需要执行的对应操作。

微分计算

基于语义分析器,可以表达微分计算函数。以下分别介绍原子处理方法及运算符列表。

(defun derivation (expr)
  "求导函数,获取S表达式,解析S表达式,输出求导后的表达式"
  (sematic expr
           ;; 导数作用于常数、变量规则
           (lambda (expr)
             (cond
               ((numberp expr) 0)
               ((symbolp expr)
                (if (eq expr 'x) 1 (error "Only x is allowded symbol. ")))
               (t (error "Neither symbol x nor a number. "))))
           ;; 导数四则运算法则
           ('+ (cons '+ evaluated-arguments))
           (- (cons '- evaluated-arguments))
           (* (cond
                ((eq 2 (length arguments))
                 (list '+
                       (list '* (car evaluated-arguments) (cadr arguments))
                       (list '* (car arguments) (cadr evaluated-arguments))))
                ; 如果是连乘列表
                ((< 2 (length arguments))
                 (derivation
                  (list '* (car arguments) (cons '* (cdr arguments)))))
                (t (error "at least 2 arguments for each operator. "))))
           (/ (cond
                ((eq 2 (length arguments))
                 (list '/
                       (list '-
                             (list '* (car evaluated-arguments) (cadr arguments))
                             (list '* (car arguments) (cadr evaluated-arguments)))
                       (list '* (cadr arguments) (cadr arguments))))
                ; 如果是连除列表
                ((< 2 (length arguments))
                 (derivation
                  (list '/ (car arguments) (cons '* (cdr arguments)))))
                (t (error "at least 2 arguments for each operator. "))))))

原子处理方法

首先讨论原子处理方法,其在本函数中表现为微分运算作用于常数、变量的规则。

  1. 微分作用于常数,所得结果为0。

    \[(C)'=0 \]

  2. 微分作用于变量\(x\),所得结果为1。其中,由于仅考虑一元表达式,x是表达式中唯一允许的符号。

    \[(x)'=1 \]

运算符列表

然后讨论运算符列表,其在本函数中表现为导数的四则运算法则

  1. 导数作用于+、-

    \[(f(x)\pm g(x))'=f'(x)\pm g'(x) \]

  2. 导数作用于乘法

    \[(f(x)g(x))'=f'(x)g+f(x)g'(x) \]

  3. 导数作用于除法

    \[\left(\frac{f(x)}{g(x)}\right)'=\frac{f'(x)g(x)-f(x)g'(x)}{g^2(x)} \]

需要注意的是,上述规则默认限定一个运算符仅进行两个数的运算。但实际Common Lisp表达式,可能是一个运算符作用于多个数字的形式,例如

(+ 1 3 5)
(* (+ 1 3 5) (- 1 3 5) (/ 48 2 6) 2)

因此,需要对上述运算法则进行一定的拓展

  1. 导数作用于+、-

    \[\left(\sum_{i=1}^k \pm f_i(x)\right)'=\pm \sum_{i=1}^k f'_i(x) \]

  2. 导数作用于乘法

    \[\left(\prod_{i=1}^k f_i(x)\right)'=f'_1(x)\prod_{i=2}^k f_i(x) + f_1(x)\left(\prod_{i=2}^k f_i(x)\right)' \]

  3. 导数作用于除法

    \[\left(\frac{f_1(x)}{\prod_{i=2}^k f_i(x)}\right)' =\frac{\displaystyle f'_1(x)\prod_{i=2}^k f_i(x)-f_1(x)\left(\prod_{i=2}^k f_i(x)\right)'} {\displaystyle\left(\prod_{i=2}^k f_i(x)\right)^2}\]

其中,导数作用于乘法、除法的处理方式又采用了递归定义。

化简运算

尽管前文定义的微分函数已经具有正常的功能,但会输出未化简结果。需要再次借助语法分析器对AST进行处理,裁剪掉多余的分支。

基于0、1的特殊性的化简

本化简函数基于0和1的特殊性,即

  1. 加法中0可以消去,若表达式中除某量外全为0,则表达式的值为该量
  2. 减法的减数中,0可以消去,若减数全为零,则表达式的值为被减数
  3. 乘法中1可以消去,若表达式中除某量外全为1,则表达式的值为该量;
    但须考虑特殊情况:
    • 当乘法中出现0,则整个表达式值为0(0乘以任何数仍为0)
  4. 除法的除数中,1可以消去,若除数全为1,则表达式的值为被除数;
    但须考虑特殊情况:
    • 首先,若除数中含有0,则应当只保留0作为除数(认为\(2\div 0\div 1 \div 2\)等于\(2\div 0\))
    • 其次,若上述情形不存在,当被除数为0时,则表达式的值为0(0除以任何非零数仍为0)
(defun simplify-01 (expr)
  "表达式化简(0和1的特殊性),消去AST多余结构
  1. +: 消去所有的0, 若为(+ 0 non-zero-or-0 0),则值为non-zero-or-0
  2. -: 消去所有的0,若为(- the-first-elem 0),则值为the-first-elem
  3. *: 消去所有的1,若为(* 1 non-one-or-1 1), 则值为non-one-for-1
        所有数乘以0仍为零
  4. /: 消去所有的1,若为(/ the-first-elem 1),则值为the-first-elem
        除数中若有零,应当只保留0;0除以所有数均为0"
  `,expr
  (sematic expr
           ;; 化简作用于原子: 无操作
           (lambda (expr) expr)
           ;; 表达式化简规则
           ('+ (let ((non-zeros
                       (remove-if (lambda(x) (eq 0 x)) evaluated-arguments)))
                 (cond ((eq 0 (length non-zeros)) 0)
                       ((eq 1 (length non-zeros)) (car non-zeros))
                       (t (cons '+ non-zeros)))))
           (- (let ((right-non-zeros
                      (remove-if (lambda(x) (eq 0 x)) (cdr evaluated-arguments)))
                    (the-first-elem (car evaluated-arguments)))
                (cond ((eq 0 (length right-non-zeros)) the-first-elem)
                      ((eq 1 (length right-non-zeros))
                       (list '- the-first-elem (car right-non-zeros)))
                      (t (cons '- (cons the-first-elem right-non-zeros))))))
           (*  (if (member 0 evaluated-arguments)
                   0
                   ; 常规规则
                   (let ((non-ones
                           (remove-if (lambda(x) (eq 1 x)) evaluated-arguments)))
                     (cond ((eq 0 (length non-ones)) 1)
                           ((eq 1 (length non-ones)) (car non-ones))
                           (t (cons '* non-ones))))))
           (/ (let ((right-non-ones
                      (remove-if (lambda(x) (eq 0 x)) (cdr evaluated-arguments)))
                    (the-first-elem (car evaluated-arguments)))
                (cond
                  ; 特殊规则
                  ((member 0 right-non-ones) (list '- the-first-elem 0))
                  ((eq 0 the-first-elem) 0)
                  ; 常规规则
                  ((eq 0 (length right-non-ones)) the-first-elem)
                  ((eq 1 (length right-non-ones))
                   (list '- the-first-elem (car right-non-ones)))
                  (t (cons '- (cons the-first-elem right-non-ones))))))))

基于四则运算特性的化简

该化简函数仍出于设计阶段,尚未完成。

(defun simplify-+-*/ (expr)
  "表达式化简(四则运算的特性)
  1. +-: + - + - => ( + + ) - ( + )
  2. */: * / * / => ( * * ) / ( * )
  3. +: 乘法结合律
  4. -: 相消
  5. /: 约分"
  `,expr
  '(do-something-here))

示例分析

运行以下示例。

; f(x) = 2*x^2 - 1/x + 5x -1
; f'(x) = 4x + 1/(x^2) +5
(defparameter f '(+ (- (* 2 x x) (/ 1 x) 1) (* 5 x)))
(defparameter |f'| (simplify-01 (derivation f)))

其中,原函数为

\[f(x)=2x^2 - \frac{1}{x}+5x-1 \]

,则导函数应当为

\[f'(x)=4x+\frac{1}{x^2}+5 \]

运行程序,结果|f'|的值为(+ (- (* 2 (+ X X)) (- (- 0 1) (* x x))) 5),符合预期。

结论

本文提出了一种基于Common Lisp的微分符号计算功能实现,并对一关于x的四则运算一元表达式示例进行验证,符合预期。验证了编译原理可以用于符号计算的实现。将来可探讨更多化简表达式的函数的规则与实现,有望实现更为完善的符号计算程序。

标签:non,right,Lisp,expr,Common,符号计算,表达式,arguments
From: https://www.cnblogs.com/suspended-monitor/p/17741193.html

相关文章

  • Go每日一库之53:commonregex
    简介有时,我们会遇到一些需要使用字符串的匹配和查找的任务。并且我们知道这种情况下,使用正则表达式是最简洁和优雅的。为了完成某个任务特地去系统地学习正则表达式费时费力,而且一段时间不用又很容易遗忘。下次遇到问题还要再重复这个过程。commonregex库来了,它内置很多常用的正......
  • Go每日一库之52:commonregex
    简介有时,我们会遇到一些需要使用字符串的匹配和查找的任务。并且我们知道这种情况下,使用正则表达式是最简洁和优雅的。为了完成某个任务特地去系统地学习正则表达式费时费力,而且一段时间不用又很容易遗忘。下次遇到问题还要再重复这个过程。commonregex库来了,它内置很多常用的正......
  • Common Certificate Formats
    为什么会有那么多种类的证书?一般而言,不同后缀的证书代表不同的编码、解码规则。要么是不同功能场景,要么是同一个功能只是不同厂商的不同风格罢了。不一一记录了,用到在查吧。Reference数字证书常见格式整理https://blog.csdn.net/zhulianhai0927/article/details/106452521......
  • CommonJS简介
    CommonJS简介Tags:JavaScript,Node.js,commonjsPublished:2023/09/26什么是commonjscommonjs是module的一种类型(规范)使用场景CommonJSismainlyusedinserver-sideJSappswithNode,asbrowsersdon'tsupporttheuseofCommonJS.CommonJS主要用于带有Node......
  • 关于hive中的com.google.common.base.Preconditions.checkArgument(ZLjava/lang/Strin
    com.google.common.base.Preconditions.checkArgument(ZLjava/lang/String;Ljava/lang/Object;)V这个报错是因为Hive 3.1.3guava19.jar和hadoop3.2.4不兼容导致 解决方法—— 之后hive就可以正常初始化了  参考博客——https://blog.csdn.net/happyfreeangel/ar......
  • .netCore 图形验证码,非System.Drawing.Common
    netcore需要跨平台,说白点就是放在windows服务器要能用,放在linux服务器上也能用,甚至macos上。很多时候需要使用到图形验证码,这就有问题了。旧方案1.引入包<PackageReferenceInclude="System.Drawing.Common"Version="5.0.3"/>2.添加引用usingSystem.Drawing;usingSystem......
  • 符号计算辅助密码学
    例题BUU-DASbook-happy#以下四行已知c=0x7a7e031f14f6b6c3292d11a41161d2491ce8bcdc67ef1baa9ee=0x872a335#q+q*p^3=1285367317452089980789441829580397855321901891350429414413655782431779727560841427444135440068248152908241981758331600586#qp+q*p^2=......
  • CommonTK框架之Qt5配置cmake脚本
    源码获取CommonTKCMake配置打开下图的CMake脚本文件添加下面的代码SET(CTK_QT_VERSION5)SET(CMAKE_PREFIX_PATH${CMAKE_PREFIX_PATH}"C:/major/development/tools/qt/5.14/install/5.14./msvc2015_64")添加的脚本代码位置如下图C:/major/development/tools/q......
  • ACL2022 paper1 CAKE: A Scalable Commonsense-Aware Framework for Multi-View Knowl
    CAKE:用于多视域知识图谱补全的可扩展常识感知框架ACL2022Abstract  知识图谱存储大规模事实三元组,然而不可避免的是图谱仍然具有不完整性。(问题)以往的只是图谱补全模型仅仅依赖于事实域数据进行实体之间缺失关系的预测,忽略了宝贵的常识知识。以往的知识图嵌入技术存在无效负......
  • Java反序列化:CommonsCollections7调试分析
    CommonsCollections7基础知识1.HashTable散列表,也称为哈希表,以key-value形式进行访问的数据结构HashTable具有线程安全:多个线程同时访问它时,不会导致数据不一致。相对于HashMap、ConcurrentHashMap等线程安全性散列表,HashTable比较古老诸如散列表,常见的类方法:putget......