web-dev-qa-db-ja.com

3点を与えられた放物線の頂点を計算する方法

放物線を形成する3つのX/Yポイントがあります。これらの3つの点を通過する放物線の頂点が何であるかを計算する必要があります。私はこれらの計算をたくさんしなければならないので、できれば迅速な方法です!

「科学者に尋ねる」ウェブサイトは この答え を提供します:

放物線の一般的な形式は、次の方程式で与えられます。A* x ^ 2 + B * x + C = yここで、A、B、およびCは任意の実定数です。 (x、y)順序対であるポイントの3つのペアがあります。各点のx値とy値を放物線の方程式に代入します。 3つの未知数、3つの定数で3つの線形方程式を取得します。次に、A、B、およびCの値についてこの3つの方程式のシステムを簡単に解くことができ、3点と交差する放物線の方程式が得られます。頂点は一次導関数が0である場所であり、小さな代数は頂点に対して:(-B/2A、C --B ^ 2/4A)を与えます。

この計算をC#またはC++で実行する実際のコードを見るとよいでしょう。誰か?

16
AZDean

これは実際には単純な線形代数の問題なので、シンボリックに計算を行うことができます。 3つのポイントのx値とy値を代入すると、3つの未知数で3つの線形方程式が得られます。

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

これを解決する簡単な方法は、行列を反転することです。

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

そしてそれをベクトルで乗算します

y1
y2
y3

この結果は...わかりました、それほど単純ではありません;-) Mathematicaでそれを行いました、そしてここに擬似コードの式があります:

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

あるいは、行列計算を数値的に実行したい場合は、通常、線形代数システム( [〜#〜] atlas [〜#〜] など)を使用しますが、 C#/ C++バインディングがあります)。

22
David Z

Davidに感謝します、私はあなたの擬似コードを次のC#コードに変換しました:

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);
}

これが私が欲しかったものです。放物線の頂点の簡単な計算。整数オーバーフローは後で処理します。

30
AZDean

直接置換により、次の3つの方程式が得られます。

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

これは行列積と同等であることに注意することで、これを解決できます。

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

したがって、行列を反転し、その逆数に右側のベクトルを乗算することで、A、B、およびCを取得できます。

私がこの投稿をしている間、ジョン・ラッシュは実際に行列方程式を解く方法をより深く掘り下げるチュートリアルにリンクしているので、それらの指示に従って答えを得ることができます。 3x3マトリックスの反転は非常に簡単なので、これはそれほど難しいことではありません。

3
kvb

@ david-zと@AZDeanのソリューションを実装するFortranのコードは次のとおりです。

subroutine parabola_vertex(x1, y1, x2, y2, x3, y3, xv, yv)
real(dp), intent(in) :: x1, y1, x2, y2, x3, y3
real(dp), intent(out) :: xv, yv
real(dp) :: denom, A, B, C
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
xv = -B / (2*A)
yv = C - B**2 / (4*A)
end subroutine
2

@AZDeanのコードに基づいて、@ piSHOCKの回答に似た何かをしました。頻繁に実行する必要がある場合(または私のようにMatlabで使用する必要がある場合)、これが最速の方法である可能性があります。

私の仮定は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
2
dzsuz87

これは宿題のようなにおいがします。 「科学者に聞く」はまさにその通りです。 3つのポイントが(x1、y1)、(x2、y2)、および(x3、y3)であるとします。次に、3つの線形方程式が得られます。

 | M11 M12 M13 | | A | | Z1 | 
 | M21 M22 M23 | * | B | = | Z2 | 
 | M31 M32 M33 | | C | | Z3 | 

ここでM11 = x12、M12 = x1、M13 = 1、Z1 = y1、および(x1、y1)の代わりに(x2、y2)および(x3、y3)を使用する他の2つの行についても同様です。

この3つの方程式のシステムを解くと、A、B、およびCの解が得られます。

1
Adam Rosenfield

https://ideone.com/y0SxK で実行

#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;
}

ネガティブなピークのユニットテストをいくつか追加しました: https://ideone.com/WGK90S でライブ実行

0
ravenspoint
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
Josh Albert