سؤال

I need a library to be able to parse an equation an give me the result giving the inputs.

For example something like this:

String equation = "x + y + z";
Map<String, Integer> vars = new HashMap<String, Integer>();
vars.add("x", 2);
vars.add("y", 1),
vars.add("z", 3);
EquationSolver solver = new EquationSolver(equation, vars);
int result = solver.getResult();
System.out.println("result: " + result);

And evaluates to: 6

Is there any kind of library for java that can do that for me?

Thanks

هل كانت مفيدة؟

المحلول

You could make use of Java 1.6's scripting capabilities:

import javax.script.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
        Map<String, Object> vars = new HashMap<String, Object>();
        vars.put("x", 2);
        vars.put("y", 1);
        vars.put("z", 3);
        System.out.println("result = "+engine.eval("x + y + z", new SimpleBindings(vars)));
    }
}

which produces:

result = 6.0

For more complex expressions, JEP is a good choice.

نصائح أخرى

There's also exp4j, an expression evaluator based on Dijkstra's Shunting Yard. It's freely available and redistributable under the Apache License 2.0, only 25kb in size and quite easy to use.

Calculable calc = new ExpressionBuilder("3 * sin(y) - 2 / (x - 2)")
        .withVariable("x", varX)
        .withVariable("y", varY)
        .build()
double result1=calc.calculate();

There's also a facility to use custom functions in exp4j.

exp4j - evaluate math expressions

have fun!

If you want high performance, I advise against using exp4j as the CogitoLearning classes are about 2600 times faster than exp4j (tested on 1 mln iterations), yes you read that right.

Often, simple expressions will suffice for business applications. Hence, the library created by CogitoLearning is probably a better choice.

Benchmark results:

1000000 iterations to evaluate 200*(1+(pi/2))^2
Time Exp4J: 1.041117999977863E-5
Time JavaScript:4.532046999924487E-5 - 0.2297235664138545x slower than Exp4j
Time ExpCogit:  4.0000000000000036E-9 - 2602.794999944655x faster than Exp4j

For Cogito library, see http://cogitolearning.co.uk/docs/cogpar/index.html

Note that: test case is not entirely pure as for evaluating JavaScript performance as I did not use a prebuilt expression for that case.

Benchmarking code used:

public class TestParser {
    private static String exprStr = "200*(1+(pi/2))^2";

    /**
     * Exp4j
     */ 
    private static ExpressionBuilder eb =  new ExpressionBuilder(exprStr);

    /**
     * Cogit
     */
    private static Parser parser = new Parser();
    private static ExpressionNode expr = parser.parse(exprStr);

    /**
     * JavaScript 
     */
    private static ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
    private static Map<String, Object> vars = new HashMap<String, Object>();

    public static void main(String[] args) throws UnknownFunctionException, UnparsableExpressionException, ScriptException {
        int n = 1000000;

        double t1 = 0d; 
        for(int i=1; i!=n; i++) {
            t1+=getCalcTimeExp4J();
        }
        double r1=t1/n;

        double t2 = 0d; 
        for(int i=1; i!=n; i++) {
            t2+=getCalcTimeCogit();
        }
        double r2=t2/n;

        double t3 = 0d; 
        for(int i=1; i!=n; i++) {
            t3+=getCalcTimeJavaScriptEngine();
        }
        double r3=t3/n;     

        System.out.println(n + " iterations to evaluate " + exprStr);
        System.out.println("Time Exp4J:\t" + r1);

        System.out.println("Time JavaScript:" + r3 + " - " + r1/r3 + "x slower than Exp4j");
        System.out.println("Time ExpCogit:\t" + r2 + " - " + r1/r2 + "x faster than Exp4j");

    }

    private static double getCalcTimeJavaScriptEngine() throws ScriptException {
        long t = Util.nanotime();

        vars.put("pi", Math.PI); 
        //Note that we're actually not using a pre-build expression here.
        engine.eval(exprStr, new SimpleBindings(vars)); 

        return(Util.nanotimeToSeconds(t));
    }

    private static double getCalcTimeCogit() {
        long t = Util.nanotime();

        expr.accept(new SetVariable("pi", Math.PI));
        double r = expr.getValue();

        return(Util.nanotimeToSeconds(t));
    }           

    private static double getCalcTimeExp4J() throws UnknownFunctionException, UnparsableExpressionException {
        long t = Util.nanotime();
        Calculable calc = eb.withVariable("pi", Math.PI).build();
        double r = calc.calculate();

        return(Util.nanotimeToSeconds(t));
    }
}

Try mXparser, below you will find usage example:

import org.mariuszgromada.math.mxparser.*;
...
...
String equation = "x + y + z";
Argument x = new Argument("x = 2");
Argument y = new Argument("y = 1");
Argument z = new Argument("z = 3");
Expression solver = new Expression(equation, x, y, z);
double result1 = solver.calculate();
System.out.println("result 1: " + result1);
x.setArgumentValue(3);
y.setArgumentValue(4);
z.setArgumentValue(5);
double result2 = solver.calculate();
System.out.println("result 2: " + result2);

Result:

result 1: 6.0
result 2: 12.0

Here the advantage of mXparser is that mXparser pre-compiles expression only once, and then, after arguments values changing, calculation is done very fast.

Follow the mXparser tutorial, mXparser math collection, mXparser API.

Regards

Eight years into the future from when this question was asked: If you don't want to re-invent the wheel, there are many exotic math parsers out there.

There is one that I wrote years ago which supports arithmetic operations, equation solving, differential calculus, integral calculus, basic statistics, function/formula definition, graphing, etc.

Its called ParserNG and its open-source.

Evaluating an expression is as simple as:

    MathExpression expr = new MathExpression("(34+32)-44/(8+9(3+2))-22"); 
    System.out.println("result: " + expr.solve());

    result: 43.16981132075472

Or using variables and calculating simple expressions:

 MathExpression expr = new MathExpression("r=3;P=2*pi*r;"); 
System.out.println("result: " + expr.getValue("P"));

Or using functions:

MathExpression expr = new MathExpression("f(x)=39*sin(x^2)+x^3*cos(x);f(3)"); 
System.out.println("result: " + expr.solve());

result: -10.65717648378352

Or to evaluate the derivative at a given point(Note it does symbolic differentiation(not numerical) behind the scenes, so the accuracy is not limited by the errors of numerical approximations):

MathExpression expr = new MathExpression("f(x)=x^3*ln(x); diff(f,3,1)"); 
System.out.println("result: " + expr.solve());

 result: 38.66253179403897

Which differentiates x^3 * ln(x) once at x=3. The number of times you can differentiate is 1 for now.

or for Numerical Integration:

MathExpression expr = new MathExpression("f(x)=2*x; intg(f,1,3)"); 
System.out.println("result: " + expr.solve());

result: 7.999999999998261... approx: 8

This parser is decently fast and has lots of other functionality.

DISCLAIMER: ParserNG is authored by me.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top