Генерация точек данных для графика из уравнения

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

1: Это тривиально, и я видел во многих приложениях.

String expr = "2*x+3*x";
Evaluator evaluator = new Evaluator();//I have this class

for (int i = start; i < end; i += step)
{
    evaluator.setConstant("x", i); 
    double ans = evaluator.evaluate(expr);
}

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

2: в настоящее время я разрабатываю второе решение. Целью этого является эффективность и будет использоваться в символических расчетах в будущем.

Пока моя реализация

Variable.java

import java.text.DecimalFormat;

public class Variable
{
    private final double pow;
    private final double coefficient;
    private final String symbol;

    public Variable(String symbol)
    {
        this.symbol = symbol;
        this.pow = 1.0;
        this.coefficient = 1.0;
    }

    public Variable(String symbol, double coefficient, double pow)throws IllegalArgumentException
    {
        if (coefficient == 0.0)throw new IllegalArgumentException("trying to create variable with coefficient 0");
        if (pow == 0.0)throw new IllegalArgumentException("trying to create variable with exponent 0");

        this.symbol = symbol;
        this.pow = pow;
        this.coefficient = coefficient;
    }

    public final String getSymbol()
    {
        return this.symbol;
    }

    public final double getPow()
    {
        return this.pow;
    }

    public final double getCoefficient()
    {
        return this.coefficient;
    }

    @Override
    public String toString()
    {
        StringBuilder builder = new StringBuilder();
        DecimalFormat decimalFormat = new DecimalFormat("#.############");
        if (coefficient != 1.0)builder.append(decimalFormat.format(this.coefficient));
        builder.append(this.symbol);
        if (this.pow != 1.0)builder.append("^").append(decimalFormat.format(this.pow));

        return builder.toString();
    }

    /*
    * Stub Method
    * Generate some unique hash code
    * such that chances of key collision
    * become less and easy to identify 
    * variables with same power and same
    * symbol*/
    @Override
    public int hashCode()
    {
        return 0;
    }
}

Equation.java

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

public class Equation
{
    private final ArrayList<Boolean> operations;
    private final HashMap<String, Variable> variableHashMap;
    private int typesOfVariables;

    public Equation(Variable variable)
    {
        this.variableHashMap = new HashMap<>();
        this.operations = new ArrayList<>();
        this.typesOfVariables = 1;

        this.variableHashMap.put(variable.getSymbol(), variable);
    }

    /*Stub Method*/
    public void addVariable(Variable variable, boolean multiply)
    {
        /*
         * Currently not covering many cases
         * 1: Add two variables which have same name
         * and same pow.
         * 2: variable which are wrapped inside functions e.g sin(x)
         * and many other.*/
        if (multiply && variableHashMap.containsKey(variable.getSymbol()))
        {
            Variable var = variableHashMap.get(variable.getSymbol());
            Variable newVar = new Variable(var.getSymbol(), var.getCoefficient() * variable.getCoefficient(), var.getPow() + variable.getPow());
            /*
             * Collision chances for variables with same name but
             * with different powers*/
            this.variableHashMap.replace(var.getSymbol(), newVar);
        }
        else
        {
            ++this.typesOfVariables;
            this.variableHashMap.put(variable.getSymbol(), variable);
        }
        this.operations.add(multiply);
    }

    /*Stub Method
     *Value for every variable at any point will be different*/
    public double solveFor(double x)
    {
        if (typesOfVariables > 1)throw new IllegalArgumentException("provide values for all variables");

        Iterator<HashMap.Entry<String, Variable>> entryIterator = this.variableHashMap.entrySet().iterator();

        Variable var;
        double ans = 0.0;
        if (entryIterator.hasNext())
        {
            var = entryIterator.next().getValue();
            ans = var.getCoefficient() * Math.pow(x, var.getPow());
        }

        for (int i = 0; entryIterator.hasNext(); i++)
        {
            var = entryIterator.next().getValue();
            if (this.operations.get(i))ans *= var.getCoefficient() * Math.pow(x, var.getPow());
            else ans += var.getCoefficient() * Math.pow(x, var.getPow());
        }
        return ans;
    }

    @Override
    public String toString()
    {
        StringBuilder builder = new StringBuilder();
        Iterator<HashMap.Entry<String, Variable>> entryIterator = this.variableHashMap.entrySet().iterator();

        if (entryIterator.hasNext())builder.append(entryIterator.next().getValue().toString());

        Variable var;
        for (int i = 0; entryIterator.hasNext(); i++)
        {
            var = entryIterator.next().getValue();
            if (this.operations.get(i))builder.append("*").append(var.toString());
            else builder.append(var.toString());
        }

        return builder.toString();
    }
}

Main.java

class Main
{
    public static void main(String[] args)
    {
        try
        {
            long t1 = System.nanoTime();

            Variable variable = new Variable("x");
            Variable variable1 = new Variable("x", -2.0, 1.0);
            Variable variable2 = new Variable("x", 3.0, 4.0);

            Equation equation = new Equation(variable);
            equation.addVariable(variable1, true);//2x+x
            equation.addVariable(variable2, true);

            for (int i = 0; i < 1000000; i++)equation.solveFor(i);//Calculate Million Data Points
            long t2 = System.nanoTime();

            System.out.println((t2-t1)/1000/1000);
            System.out.println(equation.toString());
        }
        catch (Exception e)
        {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

Я иду в правильном направлении? Есть ли какой-либо обычно используемый алгоритм для этой проблемы?

Моя главная цель - эффективность, чистота кода и удобство сопровождения кода.

Примечание: я не являюсь носителем английского языка, поэтому, пожалуйста, игнорируйте любую грамматическую ошибку.

Благодарю.

1 ответ

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

Один из самых больших экранов, которые я видел, был 2560x1440 пикселей, что означает, что большую часть времени вам понадобится менее 2500 точек, чтобы нарисовать там свой график.

Если вы указываете на чистоту кода и удобство сопровождения кода, то, скорее всего, код, состоящий из 5 строк, лучше, чем код, состоящий из 200.

Другие вопросы по тегам