20

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

Alfredo Osorio
  • 11,297
  • 12
  • 56
  • 84
  • I don't think there is a prewritten library for that, although honestly I don't think it would really be that hard for you to write yourself... – Dartoxian Jan 13 '11 at 15:51
  • What mathematical functions are you trying to preform? Java can evaluate `2+1+3`. Therefore unless your trying to do high level math, it's really just some string manipulation, and then have java evaluate the equation. Unless I misunderstand your question. – kralco626 Jan 13 '11 at 15:54
  • 4
    @Dartoxian - writing an equation parser that handles anything more than a couple of simple formats would in fact be very hard – Richard H Jan 13 '11 at 15:55
  • I am trying to define some formula in my application in order to change it dynamically through some external property. For example maybe today my formula is just "x + y + z" but then someone might want to change it to "x ^ 2 * y / z". – Alfredo Osorio Jan 13 '11 at 15:58
  • Actually it's not very hard. You convert to postfix notation and then calculate using a stack. I remember having to do that as a homework assignment to learn lex/yacc. – JOTN Jan 13 '11 at 16:01
  • @Alfredo - Bart's solution should do that. You essentially just want to replace the variables with numbers and evaluate. But why does the formula have to be a string? and way you could use vaiables without the string? `a = 2; b = 3; c = 4; result = a + b + c;` – kralco626 Jan 13 '11 at 16:03
  • @kralco626 It has to be string because it is going to be defined outside the application through an external resource like resourcebundle properties or database. What I am trying to achieve is not to hardcode the formula inside my application. – Alfredo Osorio Jan 13 '11 at 16:11

5 Answers5

30

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.

Bart Kiers
  • 166,582
  • 36
  • 299
  • 288
  • Good solution. I agree that I think all he has to do is some string manipulation and then evaluate the result. I didn't know you could do it this easily though :) +1 – kralco626 Jan 13 '11 at 16:00
  • i tried this too for a while, but evaluating as a javascript expression is quite, quite slow. so if you're doing lots of calculations this might not be the best possible solution – fasseg Nov 15 '11 at 08:33
  • Thanks for saving me lots of time for coding an equation parser; although Nashorn JavaScript engine has been removed from Java in JDK 15 and later, I can still use other JavaScript engines such as Mozilla Rhino, which was the default JavaScript engine before JDK 8's introduction of Nashorn. – Tech Expert Wizard Nov 29 '20 at 23:07
18

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!

fasseg
  • 17,504
  • 8
  • 62
  • 73
  • Piece of advice: do not post the same answers over and over again to promote your library, as that can be seen as annoying and they will be flagged as spam. If it is really good, you shouldn't need to do that at all, your happy users will spread the word for you. Cheers. – fortran Nov 15 '11 at 10:27
  • 7
    well im answering questions, in my mind that's not spamming, even if the same answer is applicable to different questions. Or do you think the answer is out of context and not helpful? – fasseg Nov 16 '11 at 08:20
  • 1
    I was just warning you that this answer was already flagged as repeated content. And as you are clearly not a spammer, I think it would be a shame that you were tagged as such. Regards. – fortran Nov 16 '11 at 09:53
  • 1
    It's not out of context. Thank you. – Alfredo Osorio Dec 27 '11 at 21:04
3

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.

Additionally - this software is using mXparser as well - you can learn the syntax Scalar Calculator app.

Regards

Leroy Kegan
  • 1,156
  • 10
  • 9
2

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));
    }
}
theDude
  • 21
  • 2
  • 2
    Note: exp4j is slow in this case because the expression is built during each iteration. When setup properly, exp4j is faster than Cogito when solving large equations that have several variables with changing/updated values during each evaluation iteration. – mstrthealias Jan 29 '15 at 18:14
  • Could you share the imports? – Andrey Mar 08 '22 at 15:06
0

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.

Community
  • 1
  • 1
gbenroscience
  • 994
  • 2
  • 10
  • 33