2

I have a table with 1000s of x- and y-coordinates. Which is the fastest way to find a matching function equation?

The table looks like this:

t - y

0 - 0.3113
1 - 0.5493
2 - 0.7190
3 - 0.6474
4 - 0.9200
5 - 0.2467
6 - 0.8068
7 - 0.5910
8 - 0.8302
9 - 0.2612
10 - 0.9869

t stands for the time, y for the amplitude.

I now want to create an equation that hits all of these points y at a certain time t. The transition from one y to another should be so smooth (linear?) as possible, also no straight lines. I want it to be in a kind of wave form like an asynchronous sinus wave.

Does anybody know how to accomplish this?

  • Look into regression. – intboolstring Mar 27 '16 at 17:55
  • 1
    Although it sounds more like a matlab job than java, here's a [cubic spline](https://commons.apache.org/proper/commons-math/jacoco/org.apache.commons.math3.analysis.interpolation/SplineInterpolator.java.html) source code. – Daniel Mar 27 '16 at 17:57

1 Answers1

0

You can use cubic splines to create cubic segments between each pair of points. The spline will ensure that you hit all the points and the continuity between the segments is higher than 0. A continuity of 0 implies that lines are used to connect the points. Below is an implementation of a Catmull-Rom spline which is one of the simplest/coolest splines out there.
Caveat
This implementation assumes x is monotonically increasing

import java.util.Arrays;

public class CatmullRomSpline
{
    protected double[] _x;
    protected double[] _y;
    public CatmullRomSpline(final double[] x, final double[] y) 
    {
        this._x = x;
        this._y = y;    
    }

    public double getValueAt(double x) 
    {
        int length = this._x.length;
        int index = Arrays.binarySearch(this._x, x);
        double result;
        // Exact match was found
        if(index >= 0)
        {
            result = this._y[index];
        }
        // x is smaller than the smaller value in the sequence
        else if(index == -1)
        {
            result = -1;
        }
        // x is larger than the largest number in the sequence
        else if(index == -length - 1)
        {
            result = -2;
        }
        // the number is between two of the numbers in the sequence
        else
        {
            index = -(index + 2);
            double p0, p1, p2, p3;
            if(index == 0)
            {
                p1 = this._y[0];
                p2 = this._y[1];
                p3 = this._y[2];
                p0 = 2 * p1 - p2;   
            }
            else if(index >= length - 2)
            {
                p0 = this._y[length - 3];
                p1 = this._y[length - 2];
                p2 = this._y[length - 1];
                p3 = 2 * p2 - p1;
            }
            else
            {
                p1 = this._y[index];
                p2 = this._y[index + 1];
                p3 = this._y[index + 2];
                p0 = this._y[index - 1];
            }
            // Normalize range from [0, 1] to agree with the derivation of the spline 
            x = (x - this._x[index]) / (this._x[index + 1] - this._x[index]);
            double c0 = p1;
            double c1 = p2 - p0;
            double c2 =  p2 - p1;
            double c3 = p1 - p3;
            double c4 = c1 - c3;
            result = c0 + x * (0.5 * c1 + x * (0.5 * c3 + 3 * c2 - c1 + x * (0.5 * c4 - 2 * c2)));
        }
        return result;
    }
    public static void main(String[] args)
    {
        // I'm fitting a parabola from t = 1 to t = 4
        double[] t = new double[] {0, 1, 2, 3, 4, 5};
        double[] y = new double[] {0, 1, 4, 9, 16, 25};

        int noPoints = 6;
        double[] tHat = linspace(1.0, 4.0, noPoints);

        CatmullRomSpline csp = new CatmullRomSpline(t, y);
        for(double value : tHat)
        {
            System.out.printf("y(t = %.4f) = %.4f\n", value, csp.getValueAt(value));
        }
        /* Output
            y(t = 1.0000) = 1.0000
            y(t = 1.5000) = 2.2500
            y(t = 2.0000) = 4.0000
            y(t = 2.5000) = 6.2500
            y(t = 3.0000) = 9.0000
            y(t = 3.5000) = 12.2500
            y(t = 4.0000) = 16.0000
        */
    }
    public static double[] linspace(double begin, double end, int noPoints)
    {
        double[] arr = new double[noPoints + 1];
        double delta = (end - begin) / noPoints;
        for(int i = 0; i < noPoints; i++)
        {
            arr[i] = begin + i * delta;
        }
        arr[noPoints] = end;
        return arr;
    }
}
StaticBeagle
  • 5,070
  • 2
  • 23
  • 34