策略简化数学EX pressions [英] Strategies for simplifying math expressions

查看:143
本文介绍了策略简化数学EX pressions的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个良好的树重新presents数学EX pression。例如,给定的字符串:1 + 2-3 * 4/5,这被分析为:

I have a well-formed tree that represents a mathematical expression. For example, given the string: "1+2-3*4/5", this gets parsed into:

subtract(add(1,2),divide(multiply(3,4),5))

这是pssed因为这棵树前$ P $:

Which is expressed as this tree:

我希望能够做的就是拿这棵树,并减少它尽可能的。在上述情况下,这是pretty的简单,因为所有的号码是常数。然而,事情开始变得棘手,一旦我允许未知(表示具有 $ 后面跟着一个标识符):

What I'd like to be able to do is take this tree and reduce it as much as possible. In the case above, this is pretty simple, because all of the numbers are constants. However, things start to get trickier once I allow for unknowns (denoted with a $ followed by an identifier):

3 * $ A / $一个变成鸿沟(乘(3,$ A),$ A)

这应该简化为 3 ,因为 $ A 方面应该相互抵消。现在的问题是,我怎么去在一个通用的方式认识到这一点?我如何识别分(3,罪($ X))总是将是罪($ X)?我如何识别 SQRT(PO​​W($ A,2)) ABS($ A)?我如何识别 nthroot(POW(42,$ A),$ A)(将一个 42根到一个功率) 42

This should simplify to 3, since the $a terms should cancel each other out. The question is, "how do I go about recognizing this in a generic manner?" How do I recognize that min(3, sin($x)) is always going to be sin($x)? How do I recognize that sqrt(pow($a, 2)) is abs($a)? How do I recognize that nthroot(pow(42, $a), $a) (the ath root of 42 to the ath power) is 42?

我意识到这个问题是pretty的广泛的,但我一直在敲打我的头这一段时间,并没有拿出任何足以令人满意。

I realize this question is pretty broad, but I've been beating my head against this for a while and haven't come up with anything satisfactory enough.

推荐答案

您可能想实现一个项重写系统 。对于潜在的数学,看看维基百科

You probably want to implement a term rewriting system. Regarding the underlying math, have a look at WikiPedia.

术语重写模块结构

因为我实现了一个解决方案,最近...

Since I implemented a solution recently...

  • 首先,prepare一类CEX pression,该款机型的前pression结构。

  • First, prepare a class CExpression, which models the structure of your expression.

实施 CRule ,其中包含一个模式和替换。使用特殊符号模式变量,这就需要模式匹配过程中得到约束,并在更换前pression取代。

Implement CRule, which contains a pattern and a replacement. Use special symbols as pattern variables, which need to get bound during pattern matching and replaced in the replacement expression.

那么,实现类 CRule 。它的主要方法 applyRule(CEX pression,CRule)试图匹配的EX pression任何适用的SUBEX pression规则。如果它匹配,返回结果。

Then, implement a class CRule. It's main method applyRule(CExpression, CRule) tries to match the rule against any applicable subexpression of expression. In case it matches, return the result.

Finaly,实现类 CRuleSet ,这简直是一组CRule对象。主要的方法减少(CEX pression)应用一套规则,只要没有规则可以应用,然后返回减少前pression。

Finaly, implement a class CRuleSet, which is simply a set of CRule objects. The main method reduce(CExpression) applies the set of rules as long as no more rules can be applied and then returns the reduced expression.

此外,你需要一个类 CBindingEnvironment ,它已经映射相匹配的匹配值的符号。

Additionally, you need a class CBindingEnvironment, which maps already matched symbols to the matched values.

尝试重写EX pression到正常形态

不要忘了,这个方法适用于一个特定的点,但很可能是不完整的。这是由于这样的事实,即所有的以下规则进行本地术语重写。

Don't forget, that this approach works to a certain point, but is likely to be non complete. This is due to the fact, that all of the following rules perform local term rewrites.

为了使这个地方重写逻辑强,应尽量改造前pressions到的东西我会打电话范式。这是我的方法:

To make this local rewrite logic stronger, one should try to transform expressions into something I'd call a normal form. This is my approach:

  • 如果一个词包含文字值,尝试移动任期靠右越好。

  • If a term contains literal values, try to move the term as far to the right as possible.

最后,该文本值可能会出现最右边,可以评价为完全字面前pression一部分。

Eventually, this literal value may appear rightmost and can be evaluated as part of a fully literal expression.

当评估完全字面EX pression

这是有趣的问题是,当评估完全字面EX pression。假设你有一个前pression

An interesting question is when to evaluate fully literal expression. Suppose you have an expression

   x * ( 1 / 3 )

这将减少到

   x * 0.333333333333333333

现在假设x被替换为3。这会产生类似于

Now suppose x gets replaced by 3. This would yield something like

   0.999999999999999999999999

因此​​急于评价返回一个稍微不正确的值。

Thus eager evaluation returns a slightly incorrect value.

在另一边,如果你把(1/3),并首次替换x 3

At the other side, if you keep ( 1 / 3 ) and first replace x by 3

   3 * ( 1 / 3 )

重写规则将使

a rewrite rule would give

   1

因此​​,它可能是到晚期评价充分字面前pression有用

Thus, it might be useful to evaluate fully literal expression late.

的重写规则的例子

下面是我的规则是如何出现在应用程序内:_1,_2,...符号匹配任何SUBEX pression:

Here is how my rules appear inside the application: The _1, _2, ... symbols match any subexpression:

addRule( new TARuleFromString( '0+_1',   // left hand side  :: pattern
                               '_1'      // right hand side :: replacement
                             ) 
       );

或比较复杂一点。

or a bit more complicated

addRule( new TARuleFromString( '_1+_2*_1', 
                               '(1+_2)*_1' 
                             ) 
       );

某些特殊符号只能满足特殊SUBEX pressions。例如。 _Literal1,_Literal2,......只匹配文字值:

Certain special symbols only match special subexpressions. E.g. _Literal1, _Literal2, ... match only literal values:

addRule( new TARuleFromString( 'exp(_Literal1) * exp(_Literal2 )', 
                               'exp( _Literal1 + _Literal2 )' 
                             ) 
       );

此规则移动非字面EX pression到左:

This rule moves non-literal expression to the left:

addRule( new TARuleFromString( '_Literal*_NonLiteral', 
                               '_NonLiteral*_Literal' 
                             ) 
       );

任何名称,以'_',是一个模式变量。虽然该系统的规则匹配,它保持了一叠已经匹配的符号分配。

Any name, that begins with a '_', is a pattern variable. While the system matches a rule, it keeps a stack of assignments of already matched symbols.

最后,不要忘了规则,可能会产生非终端置换序列。 因此,虽然降低了前pression,使这一过程要记住,这中间EX pressions已经达到之前。

Finally, don't forget that rules may yield non terminating replacement sequences. Thus while reducing expression, make the process remember, which intermediate expressions have already been reached before.

在我的实现,我不直接保存中间EX pressions。我一直MD5()的中间EX pression哈希值。数组

In my implementation, I don't save intermediate expressions directly. I keep an array of MD5() hashes of intermediate expression.

一组规则为起点

下面是一组规则开始:

            addRule( new TARuleFromString( '0+_1', '_1' ) );
            addRule( new TARuleFromString( '_Literal2=0-_1', '_1=0-_Literal2' ) );
            addRule( new TARuleFromString( '_1+0', '_1' ) );

            addRule( new TARuleFromString( '1*_1', '_1' ) );
            addRule( new TARuleFromString( '_1*1', '_1' ) );

            addRule( new TARuleFromString( '_1+_1', '2*_1' ) );

            addRule( new TARuleFromString( '_1-_1', '0' ) );
            addRule( new TARuleFromString( '_1/_1', '1' ) );

            // Rate = (pow((EndValue / BeginValue), (1 / (EndYear - BeginYear)))-1) * 100 

            addRule( new TARuleFromString( 'exp(_Literal1) * exp(_Literal2 )', 'exp( _Literal1 + _Literal2 )' ) );
            addRule( new TARuleFromString( 'exp( 0 )', '1' ) );

            addRule( new TARuleFromString( 'pow(_Literal1,_1) * pow(_Literal2,_1)', 'pow(_Literal1 * _Literal2,_1)' ) );
            addRule( new TARuleFromString( 'pow( _1, 0 )', '1' ) );
            addRule( new TARuleFromString( 'pow( _1, 1 )', '_1' ) );
            addRule( new TARuleFromString( 'pow( _1, -1 )', '1/_1' ) );
            addRule( new TARuleFromString( 'pow( pow( _1, _Literal1 ), _Literal2 )', 'pow( _1, _Literal1 * _Literal2 )' ) );

//          addRule( new TARuleFromString( 'pow( _Literal1, _1 )', 'ln(_1) / ln(_Literal1)' ) );
            addRule( new TARuleFromString( '_literal1 = pow( _Literal2, _1 )', '_1 = ln(_literal1) / ln(_Literal2)' ) );
            addRule( new TARuleFromString( 'pow( _Literal2, _1 ) = _literal1 ', '_1 = ln(_literal1) / ln(_Literal2)' ) );

            addRule( new TARuleFromString( 'pow( _1, _Literal2 ) = _literal1 ', 'pow( _literal1, 1 / _Literal2 ) = _1' ) );

            addRule( new TARuleFromString( 'pow( 1, _1 )', '1' ) );

            addRule( new TARuleFromString( '_1 * _1 = _literal', '_1 = sqrt( _literal )' ) );

            addRule( new TARuleFromString( 'sqrt( _literal * _1 )', 'sqrt( _literal ) * sqrt( _1 )' ) );

            addRule( new TARuleFromString( 'ln( _Literal1 * _2 )', 'ln( _Literal1 ) + ln( _2 )' ) );
            addRule( new TARuleFromString( 'ln( _1 * _Literal2 )', 'ln( _Literal2 ) + ln( _1 )' ) );
            addRule( new TARuleFromString( 'log2( _Literal1 * _2 )', 'log2( _Literal1 ) + log2( _2 )' ) );
            addRule( new TARuleFromString( 'log2( _1 * _Literal2 )', 'log2( _Literal2 ) + log2( _1 )' ) );
            addRule( new TARuleFromString( 'log10( _Literal1 * _2 )', 'log10( _Literal1 ) + log10( _2 )' ) );
            addRule( new TARuleFromString( 'log10( _1 * _Literal2 )', 'log10( _Literal2 ) + log10( _1 )' ) );

            addRule( new TARuleFromString( 'ln( _Literal1 / _2 )', 'ln( _Literal1 ) - ln( _2 )' ) );
            addRule( new TARuleFromString( 'ln( _1 / _Literal2 )', 'ln( _Literal2 ) - ln( _1 )' ) );
            addRule( new TARuleFromString( 'log2( _Literal1 / _2 )', 'log2( _Literal1 ) - log2( _2 )' ) );
            addRule( new TARuleFromString( 'log2( _1 / _Literal2 )', 'log2( _Literal2 ) - log2( _1 )' ) );
            addRule( new TARuleFromString( 'log10( _Literal1 / _2 )', 'log10( _Literal1 ) - log10( _2 )' ) );
            addRule( new TARuleFromString( 'log10( _1 / _Literal2 )', 'log10( _Literal2 ) - log10( _1 )' ) );


            addRule( new TARuleFromString( '_Literal1 = _NonLiteral + _Literal2', '_Literal1 - _Literal2 = _NonLiteral' ) );
            addRule( new TARuleFromString( '_Literal1 = _NonLiteral * _Literal2', '_Literal1 / _Literal2 = _NonLiteral' ) );
            addRule( new TARuleFromString( '_Literal1 = _NonLiteral / _Literal2', '_Literal1 * _Literal2 = _NonLiteral' ) );
            addRule( new TARuleFromString( '_Literal1 =_NonLiteral - _Literal2',  '_Literal1 + _Literal2 = _NonLiteral' ) );

            addRule( new TARuleFromString( '_NonLiteral + _Literal2 = _Literal1 ', '_Literal1 - _Literal2 = _NonLiteral' ) );
            addRule( new TARuleFromString( '_NonLiteral * _Literal2 = _Literal1 ', '_Literal1 / _Literal2 = _NonLiteral' ) );
            addRule( new TARuleFromString( '_NonLiteral / _Literal2 = _Literal1 ', '_Literal1 * _Literal2 = _NonLiteral' ) );
            addRule( new TARuleFromString( '_NonLiteral - _Literal2 = _Literal1',  '_Literal1 + _Literal2 = _NonLiteral' ) );

            addRule( new TARuleFromString( '_NonLiteral - _Literal2 = _Literal1 ', '_Literal1 + _Literal2 = _NonLiteral' ) );
            addRule( new TARuleFromString( '_Literal2 - _NonLiteral = _Literal1 ', '_Literal2 - _Literal1 = _NonLiteral' ) );

            addRule( new TARuleFromString( '_Literal1 = sin( _NonLiteral )', 'asin( _Literal1 ) = _NonLiteral' ) );
            addRule( new TARuleFromString( '_Literal1 = cos( _NonLiteral )', 'acos( _Literal1 ) = _NonLiteral' ) );
            addRule( new TARuleFromString( '_Literal1 = tan( _NonLiteral )', 'atan( _Literal1 ) = _NonLiteral' ) );

            addRule( new TARuleFromString( '_Literal1 = ln( _1 )', 'exp( _Literal1 ) = _1' ) );
            addRule( new TARuleFromString( 'ln( _1 ) = _Literal1', 'exp( _Literal1 ) = _1' ) );

            addRule( new TARuleFromString( '_Literal1 = _NonLiteral', '_NonLiteral = _Literal1' ) );

            addRule( new TARuleFromString( '( _Literal1 / _2 ) = _Literal2', '_Literal1 / _Literal2 = _2 ' ) );

            addRule( new TARuleFromString( '_Literal*_NonLiteral', '_NonLiteral*_Literal' ) );
            addRule( new TARuleFromString( '_Literal+_NonLiteral', '_NonLiteral+_Literal' ) );

            addRule( new TARuleFromString( '_Literal1+(_Literal2+_NonLiteral)', '_NonLiteral+(_Literal1+_Literal2)' ) );
            addRule( new TARuleFromString( '_Literal1+(_Literal2+_1)', '_1+(_Literal1+_Literal2)' ) );

            addRule( new TARuleFromString( '(_1*_2)+(_3*_2)', '(_1+_3)*_2' ) );
            addRule( new TARuleFromString( '(_2*_1)+(_2*_3)', '(_1+_3)*_2' ) );

            addRule( new TARuleFromString( '(_2*_1)+(_3*_2)', '(_1+_3)*_2' ) );
            addRule( new TARuleFromString( '(_1*_2)+(_2*_3)', '(_1+_3)*_2' ) );

            addRule( new TARuleFromString( '(_Literal * _1 ) / _Literal', '_1' ) );
            addRule( new TARuleFromString( '(_Literal1 * _1 ) / _Literal2', '(_Literal1 * _Literal2 ) / _1' ) );

            addRule( new TARuleFromString( '(_1+_2)+_3', '_1+(_2+_3)' ) );
            addRule( new TARuleFromString( '(_1*_2)*_3', '_1*(_2*_3)' ) );

            addRule( new TARuleFromString( '_1+(_1+_2)', '(2*_1)+_2' ) );

            addRule( new TARuleFromString( '_1+_2*_1', '(1+_2)*_1' ) );

            addRule( new TARuleFromString( '_literal1 * _NonLiteral = _literal2', '_literal2 / _literal1 = _NonLiteral' ) );
            addRule( new TARuleFromString( '_literal1 + _NonLiteral = _literal2', '_literal2 - _literal1 = _NonLiteral' ) );
            addRule( new TARuleFromString( '_literal1 - _NonLiteral = _literal2', '_literal1 - _literal2 = _NonLiteral' ) );
            addRule( new TARuleFromString( '_literal1 / _NonLiteral = _literal2', '_literal1 * _literal2 = _NonLiteral' ) );

订立规则一流EX pressions

这是有趣的问题:由于上述规则是特殊EX pression,它得到正确的EX pression分析器评估,用户甚至可以添加新的规则,从而提高应用程序的重写功能。

An interesting point: Since the above rules are special expression, which get correctly evaluate by the expression parser, users can even add new rules and thus enhance the application's rewrite capabilities.

解析EX pressions(或更一般的:语言)

对于可可/ OBjC应用 戴夫·德隆的DDMathParser 是一个完美的候选人,以语法分析的数学前pressions。

For Cocoa/OBjC applications, Dave DeLong's DDMathParser is a perfect candidate to syntactically analyse mathematical expressions.

有关其他的语言,我们的老朋友莱克斯和放大器; YACC 或较新的 GNU野牛可能会有所帮助。

For other languages, our old friends Lex & Yacc or the newer GNU Bison might be of help.

远年轻,有 enourmous设置就可以使用语法文件,的ANTLR 是基于Java的现代化解析器生成。除了纯粹的命令行使用, ANTLRWorks 提供了 GUI前端,以构建和调试ANTLR根据分析器。 ANTLR生成语法的各种宿主语言,喜欢的 JAVA,C,Python和PHP或C#。 ActionScript运行时是当前<一个href="http://stackoverflow.com/questions/6480598/problems-building-antlr-v3-3-from-source-antlr3-maven-archetype-missing/6481934#comment-7627243">broken.

Far younger and with an enourmous set of ready to use syntax-files, ANTLR is a modern parser generator based on Java. Besides purely command-line use, ANTLRWorks provides a GUI frontend to construct and debug ANTLR based parsers. ANTLR generates grammars for various host language, like JAVA, C, Python, PHP or C#. The ActionScript runtime is currently broken.

如果你想为学习如何分析前pressions 从底向上(或一般的语言),我会提出这样的(或德国图书版//www.ethoberon.ethz.ch/WirthPubl/CBEAll.pdf">free书的文本),Pascal和Modula-2的,著名的发明家

In case you'd like to learn how to parse expressions (or languages in general) from the bottom-up, I'd propose this free book's text from Niklaus Wirth (or the german book edition), the famous inventor of Pascal and Modula-2.

这篇关于策略简化数学EX pressions的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆