Writing a function that "solves" an equation

12,500

Solution 1

Based on your example, I'd say that you want to "evaluate an expression", rather than "solve an equation". For evaluating an expression, you can probably find many tutorials. I'll break it down in brief though. You need to do a few steps.

Starting with your string "1 + x * x", you need to break it into tokens. Specifically, break it down into: "1", "+", "x", "*", "x". At this point, you can substitute your variables ("x") for their literal values ("2"), giving you "1", "+", "2", "*", "2"

Now you need to parse the expression. Based on order of operations PEMDAS you need to create a tree data structure, where parenthetical clauses (stuff surrounded by parenthesis) are executed first, multiplication and division next, and then additions and subtraction last. Parsing is often not an easy task, and you may want to put together a simpler BNF grammar (though you can probably find a grammar for simple math expressions with some googling).

Next, walk the tree, depth first, evaluating the operations as you go up the tree. Once you get to the top of the tree, you have your solution.

If instead you want to "solve an equation", you're going to need something much more sophisticated, like Sage

Solution 2

I have used this expression evaluator before. It seemed to work very well. It allows you to pass expressions into a Parser that returns a function object that can then evaluate inputs.

var expr = Parser.parse("2 ^ x");
expr.evaluate({ x: 3 }); // 8

It supports trig functions (sin, cos, ect...) and other handy built in functions such as abs & ciel.

var expr = Parser.parse("sin(x/2) + cos(x/2)")
expr.evaluate({x: Math.PI / 2}); // 1

Examples: http://silentmatt.com/javascript-expression-evaluator/

Code: https://github.com/silentmatt/js-expression-eval

Note that this lib does not use eval().

Solution 3

Not sure I entirely understand your question but how about:

var makeFunctionOfX = function(src) { 
    return Function('x', 'return ' + src); 
};

Then you can say things like:

var g = makeFunctionOfX('2 * x')

var y = g(3); // y contains 6

The great advantage of this over eval is that the Function we create has no magic ability to see variables in the scope (hence the need to explicitly pass it x as a parameter name).

Solution 4

Using eval is safe if you trust the input from the user, and works just fine. (I have no idea what you mean by "the var tmp will still have the string this.equation".)

function FuncCreator(eqn){ this.eqn = eqn }
FuncCreator.prototype.run = function(x,y,z){ return eval(this.eqn) }

var add1 = new FuncCreator('1+x');
var result = add1.run(41); // 42

var complex = new FuncCreator('Math.sin(x*y) / Math.cos(z)');
var result = complex.run(3,4,5); // -1.891591285331882

If you don't trust the user input, you'll need to actually parse the input and process it yourself. This is non-trivial.

Solution 5

You can use the expression parser from the math.js library and do something like this:

var parser = math.parser();
var f = parser.eval('function f(x) = 1 + x * x');

// use the created function f in expressions:
parser.eval('z = 2');    // 2
parser.eval('y = f(z)'); // 5

// or use the created function f in JavaScript:
var z = 2;               // 2
var y = f(z);            // 5

Creating functions in math.js is quite currently limited, loops and blocks needed to define more extensive functions are not yet supported.

Share:
12,500

Related videos on Youtube

replax
Author by

replax

Updated on July 14, 2022

Comments

  • replax
    replax almost 2 years

    I want to write a function which will allow me to "solve" an equation in js.

    what I want (not in a programming language):

    function f(x) { 1 + x * x }
    var z = 2
    var y = f(z)  //y will be 5 as a number
    

    what I have written in JS:

    function P(cfg) { ....
    this.equation = "1 + x";
    ....};
    P.prototype.eqn = function(x) {
        var tmp = eval(this.equation);
        return tmp;
    };
    ....
    P.prototype.draw = function() {....
    for(var i = 0; i < z; i++)
        ctx.lineTo(i, this.eqn(i));
    ....};
    

    also I've read that using eval in a loop is probably not a good idea, but I have not figured out another way (yet) (JS beginner)...

    The problem with this code is, that at least in FF the var tmp will STILL contain the string from this.equation instead of the calculated value.

    I would appreciate any further insight very much!

    Thank you for your time :)

    EDIT: because my question was not formulated very well: after the execution of line var tmp = eval(this.equation); the var tmp will hold a STRING which equals the string this.equation, instead of the desired solution y value. Also I do not mean solve but evaluate, thanks for that tip :)

    • Sébastien Renauld
      Sébastien Renauld almost 11 years
      Non-trivial problem. I suggest you look up infix and postfix notations and write a parser for it. This will greatly simplify your issue.
  • Phrogz
    Phrogz almost 11 years
    Note that this is no safer than using eval.
  • Daniel Earwicker
    Daniel Earwicker almost 11 years
    @Phrogz - see the part I just added.
  • Phrogz
    Phrogz almost 11 years
    It may not have access to certain closure values, but it still has access to the full global scope. All the problems associated with calling eval are present with this solution. (But, +1, for it is more elegant than mine.)
  • replax
    replax almost 11 years
    Thank you for your answer, what I ment was, if I set breakpoints in FF and look at the value which the var tmp holds after it has been assiged to eval(...) it does STILL hold the value of the string this.equation as in tmp === this.equation //true And i do not have any idea why...