javascript 编写一个“求解”方程的函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16448800/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 04:42:46  来源:igfitidea点击:

Writing a function that "solves" an equation

javascriptmath

提问by replax

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

我想编写一个函数,它可以让我在 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:

我用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)...

我也读过在循环中使用 eval 可能不是一个好主意,但我还没有想出另一种方法(还)(JS初学者)......

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.

这段代码的问题是,至少在 FF 中,var tmp 仍然包含来自 this.equation 的字符串而不是计算值。

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 :)

编辑:因为我的问题不是很好地表述:在执行 line var tmp = eval(this.equation); 之后;var tmp 将保存一个等于字符串 this.equation 的 STRING,而不是所需的解 y 值。另外,我的意思不是解决而是评估,谢谢你的提示:)

采纳答案by Brent

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"

从字符串“1 + x * x”开始,您需要将其分解为标记。具体来说,把它分解成:"1", "+", "x", "*", "x"。此时,您可以将变量 ("x") 替换为它们的字面值 ("2"),从而为您提供"1", "+", "2", "*", "2"

Now you need to parse the expression. Based on order of operations PEMDASyou 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).

现在您需要解析表达式。根据PEMDAS的操作顺序,您需要创建一个树数据结构,其中首先执行括号子句(被括号包围的内容),然后是乘法和除法,最后是加法和减法。解析通常不是一件容易的事,您可能想要组合一个更简单的 BNF 语法(尽管您可能可以通过一些谷歌搜索找到简单数学表达式的语法)。

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

相反,如果你想“解决一个方程”,你将需要更复杂的东西,比如Sage

回答by JeremyFromEarth

I have used this expression evaluatorbefore. 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.

它支持三角函数(sin、cos、ect...)和其他方便的内置函数,如 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/

示例:http: //silentmatt.com/javascript-expression-evaluator/

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

代码:https: //github.com/silentmatt/js-expression-eval

Note that this lib does not use eval().

请注意,此库不使用 eval()。

回答by Jos de Jong

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

您可以使用math.js库中的表达式解析器并执行以下操作:

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.

在 math.js 中创建函数目前非常有限,尚不支持定义更广泛函数所需的循环和块。

回答by Daniel Earwicker

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 evalis that the Functionwe create has no magic ability to see variables in the scope (hence the need to explicitly pass it xas a parameter name).

这样做的最大优点evalFunction我们创建的没有神奇的能力来查看作用域中的变量(因此需要将其x作为参数名显式传递)。

回答by Phrogz

Using evalis 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".)

eval如果您信任用户的输入,则使用是安全的,并且工作正常。(我不知道你说的“var tmp 仍然会有字符串 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.

如果您不信任用户输入,则需要实际解析输入并自行处理。这是不平凡的。

回答by Paul Ishak

This is an old thread, but I wrote this equation calculator, this doesn't solve algebraic equations though. There is however a function that will allow you to provide an array containing assigned variables. But this doesn't solve for variables that don't have an assigned value.

这是一个旧线程,但我写了这个方程计算器,但这并不能解决代数方程。然而,有一个函数可以让您提供一个包含分配变量的数组。但这并不能解决没有赋值的变量。

I probably haven't permuted every test case scenario, but it seems to work pretty decent.

我可能没有对每个测试用例场景进行排列,但它似乎工作得相当不错。

Edit: This would have to be modified to handle negative numbers. Other than that... works fine.

编辑:这将不得不修改以处理负数。除此之外......工作正常。

Here is a fiddle

这是一个小提琴

<!doctype html>
<html>
    <head>
        <title>Javascript Equation Calculator</title>
    </head>

    <body>
        <input type="button" onclick="main()" value="calculate"><br>
        <input type="text" id="userinput"><br>
        <span id="result">Ready.</span><br>
        <script>
            function Calculator(){}
            String.prototype.replaceLast = function (what, replacement)
            {
                var pcs = this.split(what);
                var lastPc = pcs.pop();
                return pcs.join(what) + replacement + lastPc;
            };
            function inS(substr, str){return (str.indexOf(substr) > -1);}
            function arrayValueOrToken(arr, key, token)
            {
                if(key in arr)
                {
                    return arr[key];
                }
                return token;
            }
            function reduceEquation(inputStr)
            {
                console.log("reduceEquation Executed-----");
                while(hasNest(inputStr))
                {
                    if(hasNest(inputStr))
                    {
                        inputStr = inputStr.replace(")(",')*(');
                        for(var i=0;i<=9;i++)
                        {
                            inputStr = inputStr.replace(i+"(",i+'*(');
                            inputStr = inputStr.replace(")"+i,')*'+i);
                        }
                        var s = inputStr.lastIndexOf("(");
                        var e =  0;
                        for(i=s;i,inputStr.length;i++){if(inputStr[i]==")"){e=i+1;break;}}
                        var eq = inputStr.substring(s,e);
                        var replace = eq;
                        eq = eq.replace(/[()]/g, '');
                        var substitution = solveEquation(eq);
                        inputStr = inputStr.replaceLast(replace,substitution);
                    }
                }
                return inputStr;
            }
            function solveEquation(eq)
            {
                console.log("solveEquation Executed-----");
                eq = doFirstOrder(eq);
                eq = doLastOrder(eq);
                return eq;
            }
            function doFirstOrder(eq)
            {
                console.log("doFirstOrder Executed-----");
                for(var i=0;i<eq.length;i++)
                {
                    if(eq[i]=="*"){eq = solve(eq,"*");return doFirstOrder(eq);}
                    if(eq[i]=='/'){eq = solve(eq,'/');return doFirstOrder(eq);}
                }
                return eq;
            }
            function doLastOrder(eq)
            {
                console.log("doLastOrder Executed-----");
                for(var i=0;i<eq.length;i++)
                {
                    if(eq[i]=="+"){eq = solve(eq,"+");return doLastOrder(eq);}
                    if(eq[i]=="-"){eq = solve(eq,"-");return doLastOrder(eq);}
                }
                return eq;
            }
            function solve(eq, operator)
            {
                var setOp = operator;
                console.log("solve Executed-----");
                var buildEq = "",var1 = true,done = false,char="";
                var operators = "+-/*";
                var ops = operators.replace(operator, '').split('');
                var a=ops[0];
                var b=ops[1];
                var c=ops[2];
                for(var i=0;i<eq.length;i++)
                {
                    char = eq[i];
                    switch(true)
                    {
                        case(char==operator):if(var1===true){var1 = false;}else{done = true;}break;
                        case(char==a):
                        case(char==b):
                        case(char==c):if(var1){char = ""; buildEq = "";}else{done = true;}
                    }
                    if(done){break;}
                    buildEq = buildEq + char;
                }
                var parts = parts = buildEq.split(operator);
                var solution = null;
                if(operator=="+"){solution = parseFloat(parts[0]) + parseFloat(parts[1]);}
                if(operator=="-"){solution = parseFloat(parts[0]) - parseFloat(parts[1]);}
                if(operator=="*"){solution = parseFloat(parts[0]) * parseFloat(parts[1]);}
                if(operator=="/"){solution = parseFloat(parts[0]) / parseFloat(parts[1]);}
                return eq.replace(buildEq, solution);
            }
            function hasNest(inputStr){return inS("(",inputStr);}
            function allNestsComplete(inputStr)
            {
                var oC = 0, cC = 0,char="";
                for(var i=0;i<inputStr.length;i++){char = inputStr[i];if(char=="("){oC+=1;}if(char==")"){cC+=1;}}
                return (oC==cC);
            }
            Calculator.prototype.calc = function(inputStr)
            {
                console.log("Calc Executed-----");
                inputStr = inputStr.replace(/ /g, "");
                inputStr = inputStr.replace(/\/g, '/');
                inputStr = inputStr.replace(/x/g, "*")
                inputStr = inputStr.replace(/X/g, "*")
                if(!allNestsComplete(inputStr)){return "Nested operations not opened/closed properly.";}
                inputStr=reduceEquation(inputStr);
                inputStr = solveEquation(inputStr);
                return inputStr;
            };
            Calculator.prototype.calcWithVars = function(inputList)
            {
                if(inputList.length < 2){return "One or more missing arguments!";}
                var vars = [];
                var assocVars = [];
                var lastVarIndex = inputList.length - 2;
                var i = 0;
                var inputStr = inputList[inputList.length-1];
                for(i=0;i<=lastVarIndex;i++)
                {
                    vars.push(inputList[i].replace(/ /g, ""));
                }
                for(i=0;i<=vars.length-1;i++)
                {
                    var vParts = vars[i].split("=");
                    var vName = vParts[0];
                    var vValue = vParts[1];
                    assocVars[vName] = vValue;
                }
                inputStr = inputStr.replace(/ /g, "");
                var eqVars = inputStr.replace(/\s+/g, ' ').replace(/[^a-zA-Z-]/g, ' ').replace(/\s\s+/g, ' ');
                if(inS(" ", eqVars))
                {
                    eqVars = eqVars.split(" ");
                }
                else{eqVars = [eqVars];}
                eqVars.sort(function(a, b){return a.length - a.length;});
                var tempTokens = [];
                var tempCount = 1;
                for(i=0;i<eqVars.length;i++)
                {
                    var eqVname = eqVars[i];
                    var substitution = arrayValueOrToken(assocVars, eqVname, "<unknown>");
                    if(substitution != "<unknown>")
                    {
                        inputStr = inputStr.replace(eqVname,substitution);
                    }
                    else
                    {
                        var tempToken = "#______#"+tempCount+"#______#";
                        tempCount++;
                        tempTokens.push(tempToken + "?" + eqVname);
                        inputStr = inputStr.replace(eqVname,tempToken);
                    }
                }
                for(i=0;i<tempTokens.length;i++)
                {
                    var tokenSet = tempTokens[i];
                    var tokenParts = tokenSet.split("?");
                    var token = tokenParts[0];
                    var variableName = tokenParts[1];
                    inputStr = inputStr.replace(token,variableName);
                }
                var answerName = "<unknown>";
                var eq = inputStr;
                if(inS("=", inputStr))
                {
                    var eqParts = inputStr.split("=");
                    answerName = eqParts[0];
                    eq = eqParts[1];
                }
                eq = this.calc(eq);
                var result = [];
                for(i=0;i<eqVars.length;i++)
                {
                    var v = arrayValueOrToken(assocVars, eqVars[i], "<unknown>");
                    if(v != "<unknown>")
                    {
                        result.push(assocVars[eqVars[i]]);
                    }
                }
                result.push(eq);
                return result;
            };
            function main()
            {
              var calculator = new Calculator();
              elUserInput = document.getElementById('userinput');
              console.log("input: "+ elUserInput.value);
              elResult = document.getElementById('result');
              equation = elUserInput.value;
              result = calculator.calc(equation);
              console.log("result: "+ result);
              elResult.innerHTML = result;
            }
        </script>
    </body>
</html>