0

我不想解方程,我的问题不是关于 Graphs and Trees Data Structures。我正在尝试根据用户给出的方程式为图形生成数据点。我想要高效的算法,易于使用且易于维护的数据结构。我有两个解决方案

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:目前我正在开发第二种解决方案。这样做的目的是提高效率,并将在未来的符号计算中使用。

到目前为止我的实现

变量.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;
    }
}

方程.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();
    }
}

主.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());
        }
    }
}

我是否朝着正确的方向前进?这个问题有没有常用的算法?

我的主要目标是效率、代码清洁度和代码可维护性。

注意:我不是以英语为母语的人,所以请忽略任何语法错误。

谢谢。

4

1 回答 1

0

我认为您的第一个代码没有任何问题。是的,您的代码可能在每一步都“像标记化、验证、转换为 RPN、准备堆栈和队列以及最后的结果计算一样重复”,但最终所有这些都只是线性步骤数。所以我看不出它是如何让它变得非常慢的。

我见过的最大屏幕之一是 2560x1440 像素,这意味着大多数时候您需要不到 2500 个点来在那里绘制图形。

如果您指出的是代码清洁度和代码可维护性,那么由 5 行组成的代码很可能比由 200 行组成的代码更好。

于 2016-02-13T21:53:13.460 回答