求两个二次曲线交点的理论依据和编程实践

简介

        最近遇到求两个椭圆交点的的问题,一番搜索发现利用线性代数的二次型(Quadratic form)相关知识可解决,于是决定编程实践。

圆锥曲线的齐次式与二次型

        椭圆是圆锥曲线(conic section)的一种,圆锥曲线又是特定条件下的二次曲线(quadratic curve)。

        二次曲线的齐次式(homogeneous form)是Ax^{2}+Bxy+Cy^{2}+Dx+Ey+F=0。也可以按二次型定义写成x^{T}A_{Q}x=0,其中x=\begin{pmatrix} x\\ y\\ 1 \end{pmatrix}A_{Q}=\begin{pmatrix} A & B/2 & D/2\\ B/2 & C & E/2\\ D/2 & E/2 & F \end{pmatrix}

        另外A_{Q}与其余子式A_{33} = \begin{pmatrix} A & B/2\\ B/2 & C \end{pmatrix}一起能够判定二次曲线什么时候是圆锥曲线以及退化情况,引用一下Wikipedia上的原文:

If detA_{Q}=0, the conic is degenerate.

If detA_{Q}\neq0 so that Q is not degenerate, we can see what type of conic section it is by computing the minor, detA_{33}:

  • Q is a hyperbola if and only if detA_{33}<0,
  • Q is a parabola if and only if detA_{33}=0, and
  • Q is an ellipse if and only if detA_{33}> 0.

In the case of an ellipse, we can distinguish the special case of a circle by comparing the last two diagonal elements corresponding to the coefficients of x^{2} and y^{2}:

  • If A = C and B = 0, then Q is a circle.

Moreover, in the case of a non-degenerate ellipse (with detA_{33}> 0 and detA_{Q}\neq0), we have a real ellipse if \left ( A+C \right )detA_{Q}<0 but an imaginary ellipse if \left ( A+C \right )detA_{Q}>0. An example of the latter is x^{2}+y^{2}+10=0, which has no real-valued solutions.

If the conic section is degenerate (detA_{Q}=0), detA_{33} still allows us to distinguish its form:

  • Two intersecting lines (a hyperbola degenerated to its two asymptotes) if and only if detA_{33}<0.
  • Two parallel straight lines (a degenerate parabola) if and only if detA_{33}=0. These lines are distinct and real if D^{2}+E^{2}>4(A+C)F, coincident if D^{2}+E^{2}=4(A+C)F, and non-existent in the real plane if D^{2}+E^{2}<4(A+C)F.
  • A single point (a degenerate ellipse) if and only if detA_{33}> 0.

The case of coincident lines occurs if and only if the rank of the 3 × 3 matrix A_{Q} is 1; in all other degenerate cases its rank is 2.  

求两个二次曲线交点的理论

        其实Wikipedia说的退化情况正好就是用于求两个二次曲线交点的后半部分理论,Wikipedia也给出了前半部分理论:

The solutions to a system of two second degree equations in two variables may be viewed as the coordinates of the points of intersection of two generic conic sections. In particular two conics may possess none, two or four possibly coincident intersection points. An efficient method of locating these solutions exploits the homogeneous matrix representation of conic sections, i.e. a 3 × 3 symmetric matrix which depends on six parameters.

The procedure to locate the intersection points follows these steps, where the conics are represented by matrices:

  • given the two conics C_{1} and C_{2}, consider the pencil of conics given by their linear combination \lambda C_{1}+\mu C_{2}.
  • identify the homogeneous parameters (\lambda ,\mu ) which correspond to the degenerate conic of the pencil. This can be done by imposing the condition that det(\lambda C_{1}+\mu C_{2})=0 and solving for \lambdaand \mu. These turn out to be the solutions of a third degree equation.
  • given the degenerate conic C_{0}, identify the two, possibly coincident, lines constituting it.
  • intersect each identified line with either one of the two original conics; this step can be done efficiently using the dual conic representation of C_{0}.
  • the points of intersection will represent the solutions to the initial equation system.

编程实践

        0、计算二次型矩阵参数

        对要求交点的两个二次曲线的参数进行转化,得到二次型矩阵Q=\begin{pmatrix} A & B & D\\ B & C & E\\ D & E & F \end{pmatrix}需要的形式,即将二次曲线表示成

Ax^{2}+2Bxy+Cy^{2}+2Dx+2Ey+F=0                          (1)

        (1)式按x为主元可整理成

Ax^{2}+2(By+D)x+Cy^{2}+2Ey+F=0                           (2)

        有关圆锥曲线一般式和齐次式之间的参数转换计算请参见我另外的一篇博文:

        1、解三次方程计算参数\lambda

        根据方程\begin{vmatrix} \lambda Q_{1}+Q_{2} \end{vmatrix} = 0计算参数\lambda,这其实是一个三次方程:

\lambda ^{3}\begin{vmatrix} Q_{1} \end{vmatrix}+\lambda ^{2}tr\begin{vmatrix} \tilde{Q_{1}}Q_{2} \end{vmatrix}+\lambda \begin{vmatrix} Q_{1}\tilde{Q_{2}} \end{vmatrix}+\begin{vmatrix} Q_{2} \end{vmatrix}=0

        其中\tilde{Q}表示方阵Q的协因数阵(cofactor matrix),tr\begin{vmatrix} Q \end{vmatrix}表示方阵Q的迹(trace)。这里细说一下Wikipedia用了\lambda ,\mu两个参数,但其实可以取\mu=1变成一个参数\lambda(可以根据矩阵行列式的性质推一下)并且求三次方程的根时只需要找到任意一个实根赋给\lambda即可而不需要求出所有实根。

        2、将\lambda代入,分类计算出实际直线,回代到二次曲线方程求出交点

        将\lambda代入,得到一个二次型方阵Q=\lambda Q_{1}+Q_{2},此时\begin{vmatrix} Q \end{vmatrix}=\begin{vmatrix} A & B & D\\ B & C & E\\ D & E & F \end{vmatrix} = 0,即

ACF+2BDE-AE^{2}-CD^{2}-B^{2}F = 0                          (3)

        根据Wikipedia的理论分类计算出两个二次曲线相交形成的交点直线系,回代到任意一个原二次曲线得到最终交点。编程实践中,考虑浮点运算的精度误差,|A_{33}|不适合直接与0比较进行分类,而需要引入eps(实践中通常eps取1e-12到1e-10之间),实际的分类要更细致:

        2.1、|A_{33}|>eps时,AC-B^{2} > 0,可知A\neq 0,因此(2)式可以变换成

A(x+\frac{By+D}{A})^{2}+\frac{AC-B^2}{A}(y+\frac{AE-BD}{AC-B^{2}})^{2}+F-\frac{D^2}{A}-\frac{(AE-BD)^{2}}{A(AC-B^{2})}=0            (4)

由(3)式可得F-\frac{D^2}{A}-\frac{(AE-BD)^{2}}{A(AC-B^{2})} = \frac{A(ACF+2BDE-AE^{2}-CD^{2}-B^{2}F)}{A(AC-B^{2})} = 0,因此有

A(x+\frac{By+D}{A})^{2}+\frac{AC-B^2}{A}(y+\frac{AE-BD}{AC-B^{2}})^{2}=0                         (5)

由于A\frac{AC-B^2}{A}同号,可见

\left\{\begin{matrix} (x+\frac{By+D}{A})^{2}=0\\ (y+\frac{AE-BD}{AC-B^{2}})^{2}=0 \end{matrix}\right.

此时唯一的交点是(\frac{BE-CD}{AC-BD},\frac{BD-AE}{AC-B^{2}}),编程实践中最好将此点回代到两个二次曲线做检验(同样地,不能严格与0比较,而是要按\begin{vmatrix} Ax^{2}+2Bxy+Cy^{2}+2Dx+2Ey+F \end{vmatrix} < eps作为标准)

        2.2、|A_{33}|<-eps时,B^{2}-AC > 0A可能为0,但与(5)式相似,下式成立:

(Ax+By+D)^{2} = (B^2-AC)(y+\frac{AE-BD}{AC-B^{2}})^{2}                       (6)

因此得出两条直线为Ax+By+D =\pm (\sqrt{B^2-AC}y-\frac{AE-BD}{\sqrt{B^2-AC}}),两直线和其中一个二次曲线求交点,回代到另外一个二次曲线做检验,最后对点去重,就能得到答案。

        2.3、eps\leqslant |A_{33}|\leqslant eps时,按B^{2}-AC = 0的情况细分:

        2.3.1、|B|< eps时,看成B = 0,因此A = 0C = 0(其实是|A|< eps|C|< eps)

假设A=0C\neq 0,结合(3)式分析一下,此时

ACF+2BDE-AE^{2}-CD^{2}-B^{2}F = -CD^{2} = 0

必有D=0,因此可以按如下方式处理:

        2.3.1.1、|A|< eps|C|< eps,则求直线2Dx+2Ey+F=0与一个二次曲线求交点,回代到另外一个二次曲线做检验;

        2.3.1.2、仅|A|< eps,则解二次方程Cy^{2}+2Ey+F=0得到y,回代到一个二次曲线求出对应的x从而得出所有交点,最后回代到另外一个二次曲线做检验;

        2.3.1.3、仅|C|< eps,则解二次方程Ay^{2}+2Dx+F=0得到x,回代到一个二次曲线求出对应的y从而得出所有交点,最后回代到另外一个二次曲线做检验。

        2.3.2、|B|> eps时,|B|\neq 0 且B^{2}=AC,可见AC> 0此时(3)式变成

AE^{2}+CD^{2} = 2BDE                                                         (7)

        不妨设A>0(否则只需要将所有系数成-1即可),可见(7)式当且仅当BDE\geq 0AE^2=CD^2时才成立。

        另一方面Ax^2+2Bxy+Cy^2 = (\sqrt{A}x+\frac{By}{\sqrt{A}})^2,则(1)式可转化成(\sqrt Ax+\frac{By}{\sqrt a}+\lambda_1)(\sqrt Ax+\frac{By}{\sqrt a}+\lambda_2)=0,即\left\{\begin{matrix} \sqrt{A}(\lambda_1+\lambda_2)=2D\\ \frac{B}{\sqrt{A}}(\lambda_1+\lambda_2)=2E\\ \lambda_1\lambda_2=F \end{matrix}\right.(这里前两式其实等价)故D^2-AF\geqslant 0时才有解。

        这两个结论相结合,得出以下处理方式:

        2.3.2.1、BDE<-eps\begin{vmatrix} AE^2-CD^2 \end{vmatrix} > epsD^2-AF < -eps,两二次曲线无实交点(可以有虚交点);

        2.3.2.2、D^2-AF \leqslant eps,由于经过2.3.2.1排除后D^2-AF \geqslant -eps,故按D^2-AF = 0处理,因此\lambda_{1}=\lambda_{2}=\frac{D}{\sqrt A},所以应该求直线Ax+By+D= 0与一个二次曲线求交点,回代到另外一个二次曲线做检验;

        2.3.2.3、D^2-AF > eps,因此\lambda_{1}= \frac{D-\sqrt {D^2-AF}}{\sqrt A},\lambda_{2}=\frac{D+\sqrt {D^2-AF}}{\sqrt A},依次求直线Ax+By+D-\sqrt {D^2-AF}= 0Ax+By+D+\sqrt {D^2-AF}= 0与一个二次曲线求交点,回代到另外一个二次曲线做检验。

Python版

        源码可在这里下载conic_sectionsicon-default.png?t=N7T8https://github.com/DarrenCai/math/tree/main/computational_geometry/conic_sections

        python版我针对椭圆写了测试数据生成脚本,测试了1200个case,结果都正常,美中不足的是交点坐标的数值解精度不高(通常小数点第3位开始就和解析解不一样了)

C++版

/*** 圆锥曲线相关**/#include <cmath>#define DBL long double
#define eps 1e-12lvoid ellipse_homogeneous(DBL a, DBL b, DBL t, DBL x, DBL y, DBL (&c)[6]) {// 根据椭圆的长短半轴、倾斜角度和中心坐标计算椭圆一般式的系数DBL si = sin(t), co = cos(t);c[0] = si/b*si/b + co/a*co/a; c[1] = 2.*(1./a/a - 1./b/b)*si*co; c[2] = co/b*co/b + si/a*si/a;c[3] = -(2.*c[0]*x + c[1]*y); c[4] = -(2.*c[2]*y + c[1]*x); c[5] = -(c[3]*x + c[4]*y)/2. - 1.;
}void ellipse_params(DBL (&c)[6], DBL &a, DBL &b, DBL &t, DBL &x, DBL &y) {// 根据椭圆一般式的系数计算长短半轴、倾斜角度和中心坐标if (c[0] < 0) for (int i=0; i<6; ++i) c[i] = -c[i];DBL p = 4.*c[0]*c[2] - c[1]*c[1], q = sqrt((c[0]-c[2])*(c[0]-c[2]) + c[1]*c[1]);DBL r = 2.*((c[0]*c[4]*c[4] - c[1]*c[3]*c[4] + c[2]*c[3]*c[3])/p - c[5]);a = sqrt(r / (c[0]+c[2]-q)); b = sqrt(r / (c[0]+c[2]+q)); t = atan2(q - c[0] + c[2], c[1]);x = (c[1]*c[4] - 2.*c[2]*c[3]) / p; y = (c[1]*c[3] - 2.*c[0]*c[4]) / p;
}void parabola_homogeneous(DBL x, DBL y, DBL f, DBL t, DBL (&c)[6]) {// 根据抛物线的顶点坐标、焦距和倾斜角度计算抛物线一般式的系数DBL si = sin(t), co = cos(t);c[0] = si*si; c[1] = -2*si*co; c[2] = co*co; c[3] = -2*c[0]*x-c[1]*y-4*f*co;c[4] = -2*c[2]*y-c[1]*x-4*f*si; c[5] = c[0]*x*x + c[1]*x*y + c[2]*y*y + 4*f*x*co + 4*f*y*si;
}void parabola_params(DBL (&c)[6], DBL &x, DBL &y, DBL &f, DBL &t) {// 根据抛物线一般式的系数计算顶点坐标、焦距和倾斜角度t = atan2(2.*c[0], -c[1]);DBL co = cos(t), si = sin(t), s = (c[0]+c[2])*co, t = (c[0]+c[2])*si;f = (c[1]*c[4]-2.*c[2]*c[3]) / (8.*c[2]*s-4.*c[1]*t);if (f < 0.) t = atan2(-2.*c[0], c[1]), f = -f, si = -si, co = -co, s = -s, t = -t;DBL d1 = c[3] + 4.*f*s, e1 = c[4] + 4.*f*t; s = 4.*f*s - d1/2.; t = 4.*f*t - e1/2.;x = (c[1]*c[5] + d1*t) / (c[1]*s - 2*c[0]*t); y = -(2.*c[0]*x + d1) / c[1];
}void hyperbola_homogeneous(DBL a, DBL b, DBL t, DBL x, DBL y, DBL (&c)[6]) {// 根据双曲线的实半轴、虚半轴、倾斜角度和中心坐标计算双曲线一般式的系数DBL si = sin(t), co = cos(t);c[0] = co/a*co/a - si/b*si/b; c[1] = 2.*(1./a/a + 1./b/b)*si*co; c[2] = si/a*si/a - co/b*co/b;c[3] = -(2.*c[0]*x + c[1]*y); c[4] = -(2.*c[2]*y + c[1]*x); c[5] = -(c[3]*x + c[4]*y)/2. - 1.;
}void hyperbola_params(DBL (&c)[6], DBL &a, DBL &b, DBL &t, DBL &x, DBL &y) {// 根据双曲线一般式的系数计算长短半轴、倾斜角度和中心坐标DBL p = 4.*c[0]*c[2] - c[1]*c[1], q = sqrt((c[0]-c[2])*(c[0]-c[2]) + c[1]*c[1]);DBL r = 2.*((c[0]*c[4]*c[4] - c[1]*c[3]*c[4] + c[2]*c[3]*c[3])/p - c[5]); t = atan2(c[2]-c[0]+q, c[1]);if (r > 0.) a = sqrt(r / (q+c[0]+c[2])), b = sqrt(r / (q-c[0]-c[2]));else a = sqrt(r / (c[0]+c[2]-q)), b = sqrt(-r / (q+c[0]+c[2]));x = (c[1]*c[4] - 2.*c[2]*c[3]) / p; y = (c[1]*c[3] - 2.*c[0]*c[4]) / p;
}void cofactor(const DBL (&a)[3][3], DBL (&c)[3][3]) {// 计算协因数阵(cofactor matrix)// https://byjus.com/maths/cofactor/c[0][0] = a[1][1]*a[2][2] - a[1][2]*a[2][1];c[0][1] = a[1][2]*a[2][0] - a[1][0]*a[2][2];c[0][2] = a[1][0]*a[2][1] - a[1][1]*a[2][0];c[1][0] = a[0][2]*a[2][1] - a[0][1]*a[2][2];c[1][1] = a[0][0]*a[2][2] - a[0][2]*a[2][0];c[1][2] = a[0][1]*a[2][0] - a[0][0]*a[2][1];c[2][0] = a[0][1]*a[1][2] - a[0][2]*a[1][1];c[2][1] = a[0][2]*a[1][0] - a[0][0]*a[1][2];c[2][2] = a[0][0]*a[1][1] - a[0][1]*a[1][0];
}void mul(const DBL (&a)[3][3], const DBL (&b)[3][3], DBL (&c)[3][3]) {c[0][0] = a[0][0]*b[0][0] + a[0][1]*b[1][0] + a[0][2]*b[2][0];c[0][1] = a[0][0]*b[0][1] + a[0][1]*b[1][1] + a[0][2]*b[2][1];c[0][2] = a[0][0]*b[0][2] + a[0][1]*b[1][2] + a[0][2]*b[2][2];c[1][0] = a[1][0]*b[0][0] + a[1][1]*b[1][0] + a[1][2]*b[2][0];c[1][1] = a[1][0]*b[0][1] + a[1][1]*b[1][1] + a[1][2]*b[2][1];c[1][2] = a[1][0]*b[0][2] + a[1][1]*b[1][2] + a[1][2]*b[2][2];c[2][0] = a[2][0]*b[0][0] + a[2][1]*b[1][0] + a[2][2]*b[2][0];c[2][1] = a[2][0]*b[0][1] + a[2][1]*b[1][1] + a[2][2]*b[2][1];c[2][2] = a[2][0]*b[0][2] + a[2][1]*b[1][2] + a[2][2]*b[2][2];
}DBL dot_product(const DBL (&a)[3][3], const DBL (&b)[3][3]) {return a[0][0]*b[0][0] + a[0][1]*b[0][1] + a[0][2]*b[0][2] +a[1][0]*b[1][0] + a[1][1]*b[1][1] + a[1][2]*b[1][2] + a[2][0]*b[2][0] + a[2][1]*b[2][1] + a[2][2]*b[2][2];
}DBL det(const DBL (&a)[3][3], const DBL (&c)[3][3]) {// 已知方阵A和其协因数阵C,计算det|A|return a[0][0]*c[0][0] + a[0][1]*c[0][1] + a[0][2]*c[0][2];
}DBL trace(const DBL (&a)[3][3], const DBL (&b)[3][3]) {// 计算方阵AB的迹// https://en.wikipedia.org/wiki/Trace_(linear_algebra)return a[0][0]*b[0][0] + a[0][1]*b[1][0] + a[0][2]*b[2][0] +a[1][0]*b[0][1] + a[1][1]*b[1][1] + a[1][2]*b[2][1] + a[2][0]*b[0][2] + a[2][1]*b[1][2] + a[2][2]*b[2][2];
}DBL cubic_root(DBL x) {return x<0. ? -pow(-x, 1./3.) : pow(x, 1./3.);
}DBL find_a_root(DBL a, DBL b, DBL c, DBL d) {// 求出一元三次方程的一个实根DBL A = b*b - 3*a*c, B = b*c - 9*a*d, C = c*c - 3*b*d, delta = B*B - 4.*A*C;if (A == 0. && B == 0.) return -c/b;if (delta > 0.) {delta = sqrt(delta);DBL y1 = A*b + 1.5*a*(delta-B), y2 = A*b - 1.5*a*(delta+B);return -(cubic_root(y1) + cubic_root(y2) + b)/3./a;} else if (delta > -eps) return -B/2./A;DBL sA = sqrt(A), t = acos((A*b-1.5*a*B)/A/sA)/3.;return -(b + 2*sA*cos(t)) / 3. / a;
}int solve_quadratic_equation(DBL a, DBL b, DBL c, DBL *x) {// 解一元二次方程(a可以为0)并返回实根数量if (a == 0.) {if (b == 0.) return 0;*x = -c/b;return 1;}DBL delta = b*b - 4.*a*c;if (delta < 0.) {if (delta < -eps) return 0;delta = 0.;}a *= 2; delta = sqrt(delta);if (delta == 0. || delta < a*eps) {*x = -b/a;return 1;}*x = (-b-delta)/a; *(++x) = (delta-b)/a;return 2;
}bool check(const DBL (&q)[3][3], DBL x, DBL y) {// 根据圆锥曲线的矩阵表示,检查点(x,y)是否在圆锥曲线上return abs(q[0][0]*x*x + 2.*q[0][1]*x*y + q[1][1]*y*y + 2.*q[0][2]*x + 2.*q[1][2]*y + q[2][2]) < eps;
}int line_conic_intersection(const DBL (&q)[3][3], DBL a, DBL b, DBL c, DBL *x, DBL *y) {// 根据圆锥曲线的矩阵表示求其与直线ax+by+c=0的交点并返回交点数量(0/1/2)if (a == 0. && b == 0.) return 0;if (abs(a) > abs(b)) {DBL d = q[0][0]*b*b - 2.*q[0][1]*a*b + q[1][1]*a*a, f = q[0][0]*c*c - 2.*q[0][2]*a*c + q[2][2]*a*a;int m = solve_quadratic_equation(d, 2.*(q[1][2]*a*a + q[0][0]*b*c - q[0][1]*a*c - q[0][2]*a*b), f, y);for (int i=0; i<m; ++i) *(x+i) = (-c - b * (*(y+i)))/a;return m;}DBL d = q[0][0]*b*b - 2.*q[0][1]*a*b + q[1][1]*a*a, f = q[1][1]*c*c - 2.*q[1][2]*b*c + q[2][2]*b*b;int m = solve_quadratic_equation(d, 2.*(q[0][2]*b*b + q[1][1]*a*c - q[0][1]*b*c - q[1][2]*a*b), f, x);for (int i=0; i<m; ++i) *(x+i) = (-c - a * (*(x+i)))/b;return m;
}bool check(const DBL (&q)[3][3], DBL (&x)[4], DBL (&y)[4], int m, int &n) {for (int i=n-1; i>=0; --i) {if (!check(q, x[m+i], y[m+i])) {if (i < n-1) x[m] = x[m+1], y[m] = y[m+1];--n; continue;}for (int j=0; j<m; ++j) if (abs(x[j]-x[m+i])<eps && abs(y[j]-y[m+i])<eps) {if (i < n-1) x[m] = x[m+1], y[m] = y[m+1];--n; break;}}
}int conics_intersection(const DBL (&p)[3][3], const DBL (&q)[3][3], DBL (&x)[4], DBL (&y)[4]) {// 根据两圆锥曲线的矩阵表示,计算二者的交点并返回交点数量(0/1/2/3/4)// 即便用了long double,交点坐标的数值解其实精度仍然不高(一般小数点第3位开始就可能和解析解不一样了)// https://en.wikipedia.org/wiki/Matrix_representation_of_conic_sections// https://en.wikipedia.org/wiki/Conic_section#Intersecting_two_conicsDBL c1[3][3], c2[3][3];cofactor(p, c1); cofactor(q, c2);DBL r = find_a_root(det(p, c1), trace(c1, q), trace(p, c2), det(q, c2));DBL a = r*p[0][0] + q[0][0], b = r*p[0][1] + q[0][1], c = r*p[1][1] + q[1][1],d = r*p[0][2] + q[0][2], e = r*p[1][2] + q[1][2], f = r*p[2][2] + q[2][2], a33 = a*c - b*b;if (a33 > eps) {x[0] = (b*e - c*d) / a33; y[0] = (b*d - a*e) / a33;return check(p, x[0], y[0]) && check(q, x[0], y[0]);} else if (a33 < -eps) {a33 = sqrt(-a33); c = (b*d - a*e) / a33;int m = line_conic_intersection(q, a, b-a33, d-c, x, y);check(p, x, y, 0, m);int n = line_conic_intersection(q, a, b+a33, d+c, x+m, y+m);check(p, x, y, m, n);return m+n;} else if (abs(b) < eps) {if (abs(a) > max(abs(c), eps)) {int t = solve_quadratic_equation(a, 2.*d, f, x), s = x[1];if (!t) return 0;int m = line_conic_intersection(q, -1., 0., x[0], x, y);check(p, x, y, 0, m);int n = t > 1 ? line_conic_intersection(q, -1., 0., s, x+m, y+m) : 0;check(p, x, y, m, n);return m+n;} else if (abs(c) > max(abs(a), eps)) {int t = solve_quadratic_equation(c, 2.*e, f, y), s = y[1];if (!t) return 0;int m = line_conic_intersection(q, 0., -1., y[0], x, y);check(p, x, y, 0, m);int n = t > 1 ? line_conic_intersection(q, 0., -1., s, x+m, y+m) : 0;check(p, x, y, m, n);return m+n;}int m = line_conic_intersection(q, 2.*d, 2.*e, f, x, y);check(p, x, y, 0, m);return m;}if (a < 0) a = -a, b = -b, c = -c, d = -d, e = -e, f = -f;DBL s = d*d - a*f;if (b*d*e < -eps || abs(a*e*e - c*d*d) > eps || s < -eps) return 0;if (s < eps) {int m = line_conic_intersection(q, a, b, d, x, y);check(p, x, y, 0, m);return m;}s = sqrt(s);int m = line_conic_intersection(q, a, b, d-s, x, y);check(p, x, y, 0, m);int n = line_conic_intersection(q, a, b, d+s, x+m, y+m);check(p, x, y, m, n);return m+n;
}

展望

        其实我之前借助常见级数展开和牛顿迭代法等运算加速技巧基于C++实现过高精度的15种运算,双目运算:add, sub, mul, div, pow, atan2,单目运算:exp, ln, sqrt, asin, acos, atan, sin, cos, tan。

UVa12413 Big Decimal Calculator-CSDN博客文章浏览阅读38次。本人学习icpc算法竞赛时自己对UVa部分题目的解题思路 add 和 ⁡sub 的计算结果在精度范围内为0时符号按照加数/减数来,但是 mul 和 div 的结果是+0,不受乘数和除数影响。exp, ln, sqrt, asin, acos, atan, sin, cos, tan。使用泰勒展开时,需要配合一些数值技巧以保证级数快速收敛。add, sub, mul, div, pow, atan2。计算atan(x)时要考虑|x|接近1时确保快速收敛的处理方式。先实现加减乘除运算,作为实现后续运算的基础。https://blog.csdn.net/hlhgzx/article/details/133325844        因此可以将求两个二次曲线交点的C++实现写出高精度版本。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/610988.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】1.从C语言转向C++

目录 一.对C的认识 二.C的关键字 三.命名空间 3.1命名空间的定义 3.2命名空间的使用 四.C的输入与输出 五.缺省参数 5.1全缺省参数 5.2半缺省参数 六.函数重载 七.引用 7.1引用的特性 7.2引用和指针的区别 八.内联函数 九.auto关键字&#xff08;C1…

Redis中的集群(七)

集群 ASK错误 ASKING命令 ASKING命令唯一要做的就是打开发送该命令的客户端的REDIS_ASKING标识&#xff0c;以下是该命令的伪代码实现: def ASKING(): # 打开标识 client.flags | REDIS_ASKING# 向客户端返回OK回复 reply("OK")在一般情况下&#xff0c;如果客户…

Matlab 实时读取串口并绘图

Matlab 实时读取串口并绘图 Vofa Vofa 是一个很好的跨平台上位机软件&#xff0c;但是它无法保存数据&#xff0c;而且作者也并没有要继续更新的意思&#xff0c;保存数据功能应该是遥遥无期了。因此本文使用 Matlab 实时读取串口数据&#xff0c;并使用 plot 函数绘制。 vo…

airtest-ios真机搭建实践

首先阅读4 ios connection - Airtest Project Docs 在Windows环境下搭建Airtest对iOS真机进行自动化测试的过程相对复杂&#xff0c;因为iOS的自动化测试通常需要依赖Mac OS系统&#xff0c;但理论上借助一些工具和服务&#xff0c;Windows用户也可以间接完成部分工作。下面是…

单例模式以及常见的两种实现模式

单例模式是校招中最常考的设计模式之一. 设计模式其实就是类似于“规章制度”&#xff0c;按照这个套路来进行操作。 单例模式能保证某个类在程序中只存在唯一 一份实例。而不会创建出多个实例&#xff0c;如果创建出了多个实例&#xff0c;就会编译报错。而不会创建出多个实…

21、矩阵-搜索二维矩阵

思路&#xff1a; 这道题很有意思 从左到有升序&#xff0c;从上到下升序&#xff0c;斜边从左上到右下也是升序&#xff0c;从右上到做下降序。 如果是从左往右依次遍历&#xff0c;就会面临一个问题向右还是向下&#xff0c;因为都是大于当前值&#xff0c;不好决断&#x…

什么是队列

队列是一种特殊类型的线性表&#xff0c;其只允许在一端进行插入操作&#xff0c;而在另一端进行删除操作。具体来说&#xff0c;允许插入的一端称为队尾&#xff0c;而允许删除的一端称为队头。这种数据结构遵循“先进先出”&#xff08;FIFO&#xff09;的原则&#xff0c;即…

数据安全之路:Databend 用户与角色管理应用

Databend 目前支持基于角色的访问控制 (RBAC) 和 自主访问控制 (DAC) 模型&#xff0c;用于访问控制功能。 通过本指南&#xff0c;我们会了解权限和角色在 Databend 中的基本概念&#xff0c;以及如何管理角色、继承角色与建立层级、设置默认角色以及所有权的重要性。这些功能…

ios包上架系列 二、Xcode打应用市场ipa包

打包的时候一定要断开网络&#xff0c;上线包名只能在打包机配置 检查是否是正式环境&#xff0c;先在模拟器上运行 1、版本名称和本号号记得在这里更改&#xff0c;否则不生效 原因 &#xff1a;info.list <string>$(FLUTTER_BUILD_NAME)</string><key>CFB…

Docker核心特征

Docker的基本概念 Dockerfile&#xff1a;制作进行的文件&#xff0c;可以理解为制作镜像的一个清单。 镜像&#xff1a;用来创建容器的安装包&#xff0c;可以理解为给电脑安装操作系统的系统镜像。 容器&#xff1a;通过镜像来创建的一套运行环境&#xff0c;一个容器里可…

solidworks electrical 2D和3D有什么区别

SolidWorks Electrical 是一款专为电气设计开发的软件工具&#xff0c;它提供了两种主要的工作环境&#xff1a;2D电气设计和3D电气集成设计。两者在功能和应用场景上存在显著的区别&#xff1a; SolidWorks Electrical 2D 设计 特点与用途&#xff1a; SolidWorks Electrica…

绿联 安装火狐浏览器(Firefox),支持访问路由器

绿联 安装火狐浏览器&#xff08;Firefox&#xff09;&#xff0c;支持访问路由器 1、镜像 linuxserver/firefox:latest 前置条件&#xff1a;动态公网IP。 已知问题&#xff1a; 直接输入中文时&#xff0c;不能完整输入&#xff0c;也可能输入法无法切换到中文&#xff0c;可…