检查两个数学表达式是不是等价

Posted

技术标签:

【中文标题】检查两个数学表达式是不是等价【英文标题】:Check if two mathematical expressions are equivalent检查两个数学表达式是否等价 【发布时间】:2018-05-16 18:11:38 【问题描述】:

我在一次采访中遇到了一个问题。我尝试解决它,但无法提出解决方案。问题是:

[已编辑] 第一部分:给你两个只有“+”运算符的表达式,检查给定的两个表达式在数学上是否等价。 例如,“A+B+C”等价于“A+(B+C)”。

第二部分:给你两个表达式,只有“+”和“-”运算符,检查给定的两个表达式在数学上是否等价。 例如“A+B-C”等价于“A-(-B+C)”。

我的思考过程:我正在考虑从给定的表达式构建表达式树并寻找某种相似性。但是我无法想出一个检查两个表达式树是否相同的好方法。

有人可以帮助我吗:) 提前谢谢!

【问题讨论】:

你想为这样的表达发明一个canonical form 【参考方案1】:

只要运算是可交换的,我建议的解决方案是分配括号运算,然后按“变量”对术语进行排序,然后在它们之间运行聚合器,您应该会得到一串因子和符号。然后只需检查一组因素。

【讨论】:

A-B =!= B-A,所以它们在这里不可交换 其实:加法是可交换的。因子符号不同:[1]A + [-1]B =!= [1] B + [-1] A 我的观点是减法不可交换所以我不确定你的答案如何适用于这个问题 减法是负数相加。 关键是你不评估变量,只评估常数因子,直到你达到每个变量只有一个术语的状态。【参考方案2】:

聚合变量计数,直到遇到左大括号,将减法视为添加否定变量。递归处理子表达式。

子表达式的内容可以直接聚合到计数中,您只需要正确考虑符号 - 无需为此任务创建实际的表达式树。代码中使用的TreeMap只是JDK中的排序映射实现。

代码利用了当前位置是Reader 状态的一部分这一事实,因此我们可以轻松地在递归调用的右括号之后继续解析,而无需以某种方式将该信息显式地交给调用者。

Java 实现(未经测试):

class Expression 
   // Count for each variable name
   Map<String, Integer> counts = new TreeMap<>();

   Expression(Srring s) throws IOException 
     this(new StringReader(s));
   

   Expression(Reader reader) throws IOException 
     int sign = 1;
     while (true) 
       int token = reader.read(); 
       switch (token) 
         case -1:  // Eof
         case ')':
           return;
         case '(':
           add(sign, new Expression(reader));
           sign = 1;
           break;
         case '+':
           break;
         case '-':
           sign = -sign;
           break;
         default:
           add(sign, String.valueOf((char) token));
           sign = 1;
           break;
       
     
   

   void add(int factor, String variable) 
     int count = counts.containsKey(variable) ? counts.get(variable) : 0;
     counts.put(count + factor, variable);
   

   void add(int sign, Expression expr) 
     for (Map.Entry<String,Integer> entry : expr.counts.entrySet()) 
       add(sign * entry.getVaue(), entry.getKey());
     
   

   void equals(Object o) 
     return (o instanceof Expression) 
        && ((Expression) o).counts.equals(counts);
   

   // Not needed for the task, just added for illustration purposes.
   String toString() 
     StringBuilder sb = new StringBuilder();
     for (Map.Entry<String,Integer> entry : expr.counts.entrySet()) 
       if (sb.length() > 0) 
         sb.append(" + ");
       
       sb.append(entry.getValue());  // count
       sb.append(entry.getKey());    // variable name
     
     return sb.toString();
   
 

比较

new Expression("A+B-C").equals(new Expression("A-(-B+C)"))

P.S:添加了一个toString() 方法来更好地说明数据结构。

应该打印1A + 1B + -1C 作为示例。

P.P.P.P.S.:修正、简化、更好的解释。

【讨论】:

【参考方案3】:

您可以从左到右解析表达式并将它们简化为规范形式以便以直接的方式进行比较;唯一的复杂之处在于,当您遇到右括号时,您需要知道其关联的左括号前面是加号还是减号;您可以为此使用堆栈;例如:

function Dictionary() 
    this.d = [];

Dictionary.prototype.add = function(key, value) 
    if (!this.d.hasOwnProperty(key)) this.d[key] = value;
    else this.d[key] += value;

Dictionary.prototype.compare = function(other) 
    for (var key in this.d) 
        if (!other.d.hasOwnProperty(key) || other.d[key] != this.d[key]) return false;
    
    return this.d.length == other.d.length;


function canonize(expression) 
    var tokens = expression.split('');
    var variables = new Dictionary();
    var sign_stack = [];
    var total_sign = 1;
    var current_sign = 1;

    for (var i in tokens) 
        switch(tokens[i]) 
            case '(' : 
                sign_stack.push(current_sign);
                total_sign *= current_sign;
                current_sign = 1;
                break;
            
            case ')' : 
                total_sign *= sign_stack.pop();
                break;
            
            case '+' : 
                current_sign = 1;
                break;
            
            case '-' : 
                current_sign = -1;
                break;
            
            case ' ' : 
                break;
            
            default : 
                variables.add(tokens[i], current_sign * total_sign);
            
        
    
    return variables;


var a = canonize("A + B + (A - (A + C - B) - B) - C");
var b = canonize("-C - (-A - (B + (-C)))");
document.write(a.compare(b));

【讨论】:

以上是关于检查两个数学表达式是不是等价的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式等价

理解成本函数的数学表达式

深入探讨关联容器的“等价”机制

P1054 等价表达式

等价表达式 2005年NOIP全国联赛提高组(栈模拟)

等价表达式