最近用Python写了一个随机向右生成数学表达式的算法。如下图所示,点一下运行就能随机生成一个二叉树形式的算术表达式。这个树形图是用“graphviz”画的,完全是它自动布局画出来的,画的还挺不错的。代码在:becomequantum (becomequantum) · GitHub
写这个代码是为了学习编译原理,学习编译原理是因为俺想写一个万能的词法和语法分析器,也就是只写一套代码,就能分析各种编程语言等形式化的语言。要实现这点,只能用上面编译原理教程里提到的方法。也就是语法分析器的代码不靠手写,而是有个万能的语法分析器,只要你给出某个语言的声明式规范,这个万能语法分析器就能帮你解析。
我知道已经有这样的轮子存在了,但我还是想自己造一个。要造这样一个轮子,首先得搞定表达式识别,其实语法分析中,也就是表达式识别最复杂,搞定它了,别的就好搞定了。但要搞定表达式识别这个事情,从上面这些教程中看,还是挺复杂的。这个表达式的产生式看着就挺复杂的,我都懒得去理解它。
我刚开始看这些视频的时候,半天都看不明白“终结符”和“非终结符”到底是啥意思,后来才发现“终结”对应的就是树里的叶子节点,“非终结”对应的就是非叶子节点,这才明白是咋回事。这个外国人啊,就是喜欢造各种新的形式化语言和新的术语,结果就是一大堆不知所云的概念把人搞得稀里糊涂。所以我决定不了这些概念,和前人整的那些算法,用自己的思路解决这个问题。
算法领域里的所有问题其实都可以归结到图里,算法的流程图也是图。简单的数组遍历也可以看成是在遍历线性图。所以可以说一切算法就是建立在图遍历的基础之上的。所以各种计算机领域的问题,你都用图里面的概念去理解就行了,这样就能看穿不同概念之间的共同特性,也就是融汇贯通了。
于是我先思考了一下表达式生成的问题,我觉得上面那个表达式的产生式不好理解,利用它生成算法我也不知道咋弄,我也懒得继续看教程去把它搞清楚,因为我觉得它把这个问题搞复杂了。我的思路是从状态机出手,假如要用状态机生成一个任意算术表达式,这个状态机会是咋样的。写了一下发现这个表达式生成状态机其实挺简单的,三个状态就行了,如上图所示。第一步和第二步都很简单,第一步就生成一个左边的叶子节点,因为是要向右生成,所以一开始的左节点就直接变为叶子,不让它继续发展出子树。第二步就是随一个运算符。第三步先随机决定一下生成是否终结,终结就生成一个右边的叶子节点。如果不终结,就会跳转到第一步继续生成,也就是形成了递归调用。只是这新生成的表达式所处的位置又分两种情况,如上图所示。
之所以只向右生成,是因为在识别表达式的时候,也是从左往右顺序进行的。等我写完这个生成代码之后就发现,其实这个生成的状态转换流程,完全也可以用在识别上,状态机的结构是一样的,只是每个状态要做的事情是“反过来的”。识别具体咋弄下次再说,俺现在还没把代码写出来。然后我有一个重要领悟就是,这个生成的算法是可逆的啊,意思就是,流程结构不变,改一下每个状态所进行的操作,生成状态机就能变成识别状态机。这又让我想起了上半年研究出来的精准线段识别算法,这个算法也可以看成是Bresenham画直线算法的逆算法,只不过这个逆算法的流程要比正向的复杂一点。这就有意思了,难倒各种生成算法都是能逆回去的?生成算法的逆算法就是识别算法,一般来说生成算法比较容易,识别算法比较难。对于计算机来说是这样的,但对于人脑来说似乎不是这样,所以我猜我们是还没有找到把各种生成算法逆回去的高效算法。等都找到了,AI识别就不需要整一堆浮点数神经元训练半天了,基于逻辑算法的AI识别效率会大大提升。
现在的AI算法其实就好比是用随机投针之类的蒙特卡洛方法算圆周率,也能算,但收敛的慢,效率很低。用公式算,尤其是用拉马努金的公式算效率会高很多。所以等基于逻辑公式的AI出现之后,就会对现在基于概率的AI形成降维打击!