Написание функции, которая "решает" уравнение

Я хочу написать функцию, которая позволит мне "решить" уравнение в JS.

что я хочу (не на языке программирования):

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

что я написал в 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));
....};

также я прочитал, что использование eval в цикле, вероятно, не является хорошей идеей, но я еще не понял другого способа (пока) (JS beginner)...

проблема с этим кодом заключается в том, что по крайней мере в FF var tmp по-прежнему будет содержать строку из этого.уравнение вместо расчетного значения.

Я был бы очень признателен за любое дальнейшее понимание!

Спасибо за ваше время :)

EDIT: потому что мой вопрос был сформулирован не очень хорошо: после выполнения строки var tmp = eval (this.уравнение); var tmp будет содержать строку, которая равна строке this.уравнение, вместо желаемого значения y решения. Также я не имею в виду решить, но оценить, спасибо за совет :)

6 ответов


основываясь на вашем примере, я бы сказал, что вы хотите "вычислить выражение", а не "решить уравнение". Для оценки выражения, вы, вероятно, можете найти многие учебники. Но вкратце разберу. Вам нужно сделать несколько шагов.

начиная с вашей строки "1 + x * x", вам нужно разбить ее на токены. В частности, разбейте его на:"1", "+", "x", "*", "x". На этом этапе вы можете заменить свои переменные ("x") на их литеральные значения ("2"), давая вам "1", "+", "2", "*", "2"

теперь вам нужно разобрать выражение. На основе порядок операций PEMDAS вам нужно создать древовидную структуру данных, где сначала выполняются парентетические предложения (материал, окруженный скобками), затем умножение и деление, а затем сложение и вычитание. Синтаксический анализ часто не является простой задачей, и вы можете собрать более простую грамматику BNF (хотя вы, вероятно, можете найти грамматику для простых математических выражений с некоторыми googling).

далее, ходить по дереву, глубина сначала, оценивая операции, как вы идете вверх по дереву. Как только вы доберетесь до вершины дерева, у вас есть решение.

Если вместо этого вы хотите "решить уравнение", вам понадобится что-то гораздо более сложное, например шалфей


я использовал Это выражение оценщика раньше. Похоже, это сработало очень хорошо. Он позволяет передавать выражения в синтаксический анализатор, который возвращает объект функции, который затем может оценивать входные данные.

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

Он поддерживает функции тригонометрии (sin, cos, ect...) и другие сподручные построенные в функциях как abs & ciel.

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

примеры:http://silentmatt.com/javascript-expression-evaluator/

код: https://github.com/silentmatt/js-expression-eval

обратите внимание, что этот lib не использует eval().


не уверен, что я полностью понимаю ваш вопрос, но как насчет:

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

затем вы можете сказать такие вещи, как:

var g = makeFunctionOfX('2 * x')

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

большое преимущество этого над eval это Function мы создаем не имеет магической способности видеть переменные в области (следовательно, необходимо явно передать его x в качестве имени параметра).


используя eval безопасно, если Вы доверяете вводу от пользователя, и работает просто отлично. (Я понятия не имею, что вы подразумеваете под " у var tmp все равно будет строка this.уравнение".)

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

Если вы не доверяете вводу пользователя, вам нужно будет фактически проанализировать ввод и обработать его самостоятельно. Это нетривиально.


вы можете использовать парсер выражений из математика.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

создание функций в математике.js в настоящее время довольно ограничен, петли и блоки, необходимые для определения более обширных функций, еще не поддерживаются.


Это старый поток, но я написал этот калькулятор уравнений, хотя это не решает алгебраические уравнения. Однако существует функция, которая позволит вам предоставить массив, содержащий назначенные переменные. Но это не решает для переменных, которые не имеют присвоенного значения.

Я, вероятно, не переставлял каждый сценарий тестового случая, но, похоже, работает довольно прилично.

Edit: это должно быть изменено для обработки отрицательных чисел. Другие, чем, что... работать отлично.

вот скрипка

<!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>