How to calculate the vertex of a parabola given three points

后端 未结 8 1016
故里飘歌
故里飘歌 2020-11-30 03:11

I have three X/Y points that form a parabola. I simply need to calculate what the vertex of the parabola is that goes through these three points. Preferably a quick way as I

相关标签:
8条回答
  • 2020-11-30 04:03

    You get the following three equations by direct substitution:

    A*x1^2+B*x1+C=y1
    A*x2^2+B*x2+C=y2
    A*x3^2+B*x3+C=y3
    

    You can solve this by noting that this is equivalent to the matrix product:

    [x1^2 x1 1] [A]   [y1]
    |x2^2 x2 1|*|B| = |y2|
    [x3^2 x3 1] [C]   [y3]
    

    So you can get A,B, and C by inverting the matrix and multiplying the inverse with the vector on the right.

    I see that while I've been posting this John Rasch has linked to tutorial that goes into more depth on actually solving the matrix equation, so you can follow those instructions to get the answer. Inverting a 3x3 matrix is quite easy, so this shouldn't be too tough.

    0 讨论(0)
  • 2020-11-30 04:03
    def vertex(x1,x2,x3,y1,y2,y3):
        '''Given three pairs of (x,y) points return the vertex of the
             parabola passing through the points. Vectorized and common expression reduced.'''
        #Define a sequence of sub expressions to reduce redundant flops
        x0 = 1/x2
        x4 = x1 - x2
        x5 = 1/x4
        x6 = x1**2
        x7 = 1/x6
        x8 = x2**2
        x9 = -x7*x8 + 1
        x10 = x0*x1*x5*x9
        x11 = 1/x1
        x12 = x3**2
        x13 = x11*x12
        x14 = 1/(x0*x13 - x0*x3 - x11*x3 + 1)
        x15 = x14*y3
        x16 = x10*x15
        x17 = x0*x5
        x18 = -x13 + x3
        x19 = y2*(x1*x17 + x14*x18*x6*x9/(x4**2*x8))
        x20 = x2*x5
        x21 = x11*x20
        x22 = x14*(-x12*x7 + x18*x21)
        x23 = y1*(-x10*x22 - x21)
        x24 = x16/2 - x19/2 - x23/2
        x25 = -x17*x9 + x7
        x26 = x0*x1*x14*x18*x5
        x27 = 1/(-x15*x25 + y1*(x20*x7 - x22*x25 + x7) + y2*(-x17 + x25*x26))
        x28 = x24*x27
        return x28,x15 + x22*y1 + x24**2*x27 - x26*y2 + x28*(-x16 + x19 + x23)
    
    0 讨论(0)
  • 2020-11-30 04:11

    This is really just a simple linear algebra problem, so you can do the calculation symbolically. When you substitute in the x and y values of your three points, you'll get three linear equations in three unknowns.

    A x1^2 + B x1 + C = y1
    A x2^2 + B x2 + C = y2
    A x3^2 + B x3 + C = y3
    

    The straightforward way to solve this is to invert the matrix

    x1^2  x1  1
    x2^2  x2  1
    x3^2  x3  1
    

    and multiply it by the vector

    y1
    y2
    y3
    

    The result of this is... okay, not exactly all that simple ;-) I did it in Mathematica, and here are the formulas in pseudocode:

    denom = (x1 - x2)(x1 - x3)(x2 - x3)
    A = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / denom
    B = (x3^2 * (y1 - y2) + x2^2 * (y3 - y1) + x1^2 * (y2 - y3)) / denom
    C = (x2 * x3 * (x2 - x3) * y1 + x3 * x1 * (x3 - x1) * y2 + x1 * x2 * (x1 - x2) * y3) / denom
    

    Alternatively, if you wanted to do the matrix math numerically, you'd typically turn to a linear algebra system (like ATLAS, though I'm not sure if it has C#/C++ bindings).

    In any case, once you have the values of A, B, and C as calculated by these formulas, you just have to plug them into the expressions given in the question, -B / 2A and C - B^2/4A, to calculate the coordinates of the vertex.1

    Note that if the original three points have coordinates that make denom a very large or very small number, doing the calculation directly might be susceptible to significant numerical error. In that case it might be better to modify it a bit, to avoid dividing by the denominators where they would cancel out anyway:

    denom = (x1 - x2)(x1 - x3)(x2 - x3)
    a = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2))
    b = (x3^2 * (y1 - y2) + x2^2 * (y3 - y1) + x1^2 * (y2 - y3))
    c = (x2 * x3 * (x2 - x3) * y1 + x3 * x1 * (x3 - x1) * y2 + x1 * x2 * (x1 - x2) * y3)
    

    and then the coordinates of the vertex are -b / 2a and (c - b^2 / 4a) / denom. There are various other situations that might benefit from "tricks" like this, such as if A is very large or very small, or if C is nearly equal to B^2 / 4A so that their difference is very small, but I think those situations vary enough that a full discussion would be better left for case-by-case followup questions.

    Converting all of this to code in the language of your choice is left as an exercise for the reader. (It should be pretty trivial in any language that uses standard infix operator syntax, e.g. as AZDean showed in C#.)


    1In the initial version of the answer I thought this would be obvious, but it seems there are a lot of people who like having it mentioned explicitly.

    0 讨论(0)
  • 2020-11-30 04:12

    I've done something similar to @piSHOCK's answer, also based on @AZDean's code. If you need to run it heavily (or to use it in Matlab like me), this might be the fastest one.

    My assumption is that x1 == -1, x2 == 0, x3 == 1.

    a = y2 - ( y1 + y3) / 2   % opposite signal compared to the original definition of A
    b = (y3 - y1) / 4         % half of the originally defined B
    
    xExtr = b / a
    yExtr = y2 + b * yExtr    % which is equal to y2 + b*b / a
    
    0 讨论(0)
  • 2020-11-30 04:14

    Running at https://ideone.com/y0SxKU

    #include <iostream>
    using namespace std;
    // calculate the vertex of a parabola given three points
    // https://stackoverflow.com/q/717762/16582
    
    // @AZDean implementation with given x values
    
    void CalcParabolaVertex(int x1, int y1, int x2, int y2, int x3, int y3, double& xv,  double& yv)
    {
        double denom = (x1 - x2) * (x1 - x3) * (x2 - x3);
        double A     = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / denom;
        double B     = (x3*x3 * (y1 - y2) + x2*x2 * (y3 - y1) + x1*x1 * (y2 - y3)) / denom;
        double C     = (x2 * x3 * (x2 - x3) * y1 + x3 * x1 * (x3 - x1) * y2 + x1 * x2 * (x1 - x2) * y3) / denom;
    
        xv = -B / (2*A);
        yv = C - B*B / (4*A);
    }
    
    // @piSHOCK immplementation assuming regular x values ( wrong!!! )
    
    void CalcParabolaVertex2( int y1, int y2, int y3, double& xv,  double& yv)
    {
    double d1 = y1 - y2;
    double d2 = y1 - y3;
    
    double a =    -d1 + 0.5 * d2;
    double b = 2 * d1 - 0.5 * d2;
    double c = -y1;
    
    xv =    -0.5      * b / a;
    yv = c - 0.25 * b * b / a;  
    }
    
    // corrected immplementation assuming regular x values
    
    void CalcParabolaVertex3( int y1, int y2, int y3, double& xv,  double& yv)
    {
    double d1 = y1 - y2;
    double d2 = y1 - y3;
    
    double a = d1 - 0.5 * d2;
    double b = -2 * d1 + 0.5 * d2;
    double c = y1;
    
    xv =    -0.5      * b / a;
    yv = c - 0.25 * b * b / a;  
    }
    
    
    int main() {
        double xv, yv;
        CalcParabolaVertex( 0, 100, 1, 500, 2, 200, xv, yv );
        cout << xv <<" "<< yv << "\n";
        CalcParabolaVertex2( 100, 500, 200, xv, yv );
        cout << xv <<" "<< yv << "\n";
        CalcParabolaVertex3( 100, 500, 200, xv, yv );
        cout << xv <<" "<< yv << "\n";
        return 0;
    }
    

    I have added a couple of unit tests for negative going peaks: running live at https://ideone.com/WGK90S

    0 讨论(0)
  • 2020-11-30 04:16

    Thanks David, I converted your pseudocode to the following C# code:

    public static void CalcParabolaVertex(int x1, int y1, int x2, int y2, int x3, int y3, out double xv, out double yv)
    {
        double denom = (x1 - x2) * (x1 - x3) * (x2 - x3);
        double A     = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / denom;
        double B     = (x3*x3 * (y1 - y2) + x2*x2 * (y3 - y1) + x1*x1 * (y2 - y3)) / denom;
        double C     = (x2 * x3 * (x2 - x3) * y1 + x3 * x1 * (x3 - x1) * y2 + x1 * x2 * (x1 - x2) * y3) / denom;
    
        xv = -B / (2*A);
        yv = C - B*B / (4*A);
    }
    

    This is what I wanted. A simple calculation of the parabola's vertex. I'll handle integer overflow later.

    0 讨论(0)
提交回复
热议问题