Algorithm Description
设
T
(
c
o
s
θ
,
s
i
n
θ
)
T(cos\theta,sin\theta)
T(cosθ,sinθ),则有
P
T
+
Q
T
=
(
P
x
−
c
o
s
θ
)
2
+
s
i
n
2
θ
+
(
Q
x
−
c
o
s
θ
)
2
+
(
Q
y
−
s
i
n
θ
)
2
PT+QT=\sqrt{(P_x-cos\theta)^2+sin^2\theta}+\sqrt{(Q_x-cos\theta)^2+(Q_y-sin\theta)^2}
PT+QT=(Px−cosθ)2+sin2θ+(Qx−cosθ)2+(Qy−sinθ)2
P
T
+
Q
T
=
P
x
2
−
2
P
x
c
o
s
θ
+
1
+
Q
x
2
+
Q
y
2
+
1
−
2
Q
x
c
o
s
θ
−
2
Q
y
s
i
n
θ
PT+QT=\sqrt{P_x^2-2P_xcos\theta+1}+\sqrt{Q_x^2+Q_y^2+1-2Q_xcos\theta-2Q_ysin\theta}
PT+QT=Px2−2Pxcosθ+1+Qx2+Qy2+1−2Qxcosθ−2Qysinθ
由费马原理,光线沿
P
T
+
Q
T
PT+QT
PT+QT最短的路径传播,因此只需对上式求导求极小值点。关于
θ
\theta
θ求导得
P
x
s
i
n
θ
P
x
2
−
2
P
x
c
o
s
θ
+
1
+
Q
x
s
i
n
θ
−
Q
y
c
o
s
θ
Q
x
2
+
Q
y
2
+
1
−
2
Q
x
c
o
s
θ
−
2
Q
y
s
i
n
θ
\frac{P_xsin\theta}{\sqrt{P_x^2-2P_xcos\theta+1}}+\frac{Q_xsin\theta-Q_ycos\theta}{\sqrt{Q_x^2+Q_y^2+1-2Q_xcos\theta-2Q_ysin\theta}}
Px2−2Pxcosθ+1Pxsinθ+Qx2+Qy2+1−2Qxcosθ−2QysinθQxsinθ−Qycosθ
故只需用二分法解非线性方程
P
x
s
i
n
θ
Q
x
2
+
Q
y
2
+
1
−
2
Q
x
c
o
s
θ
−
2
Q
y
s
i
n
θ
+
(
Q
x
s
i
n
θ
−
Q
y
c
o
s
θ
)
P
x
2
−
2
P
x
c
o
s
θ
+
1
=
0
P_xsin\theta\sqrt{Q_x^2+Q_y^2+1-2Q_xcos\theta-2Q_ysin\theta}+(Q_xsin\theta-Q_ycos\theta)\sqrt{P_x^2-2P_xcos\theta+1}=0
PxsinθQx2+Qy2+1−2Qxcosθ−2Qysinθ+(Qxsinθ−Qycosθ)Px2−2Pxcosθ+1=0
T
x
=
c
o
s
θ
T_x=cos\theta
Tx=cosθ
T
y
=
s
i
n
θ
T_y=sin\theta
Ty=sinθ
由对称性易知
R
x
=
2
Q
y
−
Q
x
t
a
n
θ
−
k
Q
x
+
k
(
2
T
x
−
Q
x
)
−
2
T
y
k
−
t
a
n
θ
Rx=\frac{2Q_y-Qxtan\theta -kQ_x +k(2Tx - Qx) - 2Ty}{k-tan\theta}
Rx=k−tanθ2Qy−Qxtanθ−kQx+k(2Tx−Qx)−2Ty
R
y
=
Q
y
−
(
Q
x
−
R
x
)
θ
Ry=Qy-(Qx - Rx)\theta
Ry=Qy−(Qx−Rx)θ
Code
#include<iostream>
#include<cmath>
#include <iomanip>
using namespace std;
int main(int argc, char *argv[])
{
if(argc != 4) {
cout << "Usage: " << argv[0] << " <Px> <Qx> <Qy>\n";
return 1;
}
long double Px = stold(argv[1]);
long double Qx = stold(argv[2]);
long double Qy = stold(argv[3]);
if(Px >= -1 || Qx >= 0 || Qy <= 0 || Qx * Qx + Qy * Qy <= 1) {
cout << "输入错误,Px应该小于-1,Qx应该小于0,Qy应该大于0,Qx^2+Qy^2应该大于1\n";
return 1;
}
long double Tx = 0;
long double Ty = 0;
long double theta = 0;
long double low = 3.14159265358979323 , high = 1.570796326794896;
long double k = 0;
while(1)
{
theta = (low + high) / 2;
long double eq1 = Px * sin(theta);
long double eq2 = sqrt(Qx * Qx + Qy * Qy +1 - 2 * Qx * cos(theta) - 2 * Qy * sin(theta));
long double eq3 = (Qx * sin(theta) - Qy * cos(theta));
long double eq4 = sqrt(Px * Px -2 * Px * cos(theta) + 1);
long double res = eq1 * eq2 + eq3 * eq4;
long double absres = abs(res);
if(absres <= 1e-7)
{
Tx = cos(theta);
Ty = sin(theta);
k = 1 / tan(3.14159265358979323 - theta);
break;
}
else if(res > 0)
{
low = theta;
}
else
{
high = theta;
}
}
long double eq5 = 2 * Qy - Qx * tan(theta) + k * (2 * Tx - Qx) - 2 * Ty;
long double eq6 = k - tan(theta);
long double Rx = eq5 / eq6;
long double Ry = Qy - tan(theta) * (Qx - Rx);
cout << "T = (" << fixed << setprecision(6) << Tx << " , " << fixed << setprecision(6) << Ty << ") , R = (" << fixed << setprecision(6) << Rx << " , " << fixed << setprecision(6) << Ry << ")";
return 0;
}
Results
P = ( − 2 , 0 ) , Q = ( − 1 , 1 ) : T = ( − 0.885670 , 0.464316 ) , R = ( − 0.380057 , 0.674993 ) P = (-2, 0), Q = (-1, 1):T = (-0.885670, 0.464316), R = (-0.380057, 0.674993) P=(−2,0),Q=(−1,1):T=(−0.885670,0.464316),R=(−0.380057,0.674993)
P = ( − 10 , 0 ) , Q = ( − 2 , 1 ) : T = ( − 0.959312 , 0.282350 ) , R = ( 0.304214 , 0.321811 ) P = (-10, 0), Q = (-2, 1): T = (-0.959312, 0.282350), R = (0.304214, 0.321811) P=(−10,0),Q=(−2,1):T=(−0.959312,0.282350),R=(0.304214,0.321811)
P = ( − 1.000001 , 0 ) , Q = ( − 2 , 2 ) : T = ( − 1.000000 , 0.000002 ) , R = ( 0.000007 , 1.999996 ) P = (-1.000001, 0), Q = (-2, 2):T = (-1.000000 , 0.000002) , R = (0.000007 , 1.999996) P=(−1.000001,0),Q=(−2,2):T=(−1.000000,0.000002),R=(0.000007,1.999996)
P = ( − 2 , 0 ) , Q = ( − 1 , 0.000001 ) : T = ( − 1.000000 , 0.000001 ) , R = ( − 1.000000 , 0.000001 ) P = (-2, 0), Q = (-1, 0.000001):T = (-1.000000 , 0.000001) , R = (-1.000000 , 0.000001) P=(−2,0),Q=(−1,0.000001):T=(−1.000000,0.000001),R=(−1.000000,0.000001)
P = ( − 2.33 , 0 ) , Q = ( − 3 , 1 ) : T = ( − 0.989279 , 0.146038 ) , R = ( 1.182424 , 0.382590 ) P = (-2.33, 0), Q = (-3, 1):T = (-0.989279 , 0.146038) , R = (1.182424 , 0.382590) P=(−2.33,0),Q=(−3,1):T=(−0.989279,0.146038),R=(1.182424,0.382590)
P = ( − 3 , 0 ) , Q = ( − 1 , 0.5 ) : T = ( − 0.922615 , 0.385721 ) , R = ( − 0.786920 , 0.410917 ) P = (-3, 0), Q = (-1, 0.5):T = (-0.922615 , 0.385721) , R = (-0.786920 , 0.410917) P=(−3,0),Q=(−1,0.5):T=(−0.922615,0.385721),R=(−0.786920,0.410917)
P = ( − 3 , 0 ) , Q = ( − 2 , 10 ) : T = ( − 0.827028 , 0.562160 ) , R = ( 8.380296 , 2.944148 ) P = (-3, 0), Q = (-2, 10):T = (-0.827028 , 0.562160) , R = (8.380296 , 2.944148) P=(−3,0),Q=(−2,10):T=(−0.827028,0.562160),R=(8.380296,2.944148)
P = ( − 3 , 0 ) , Q = ( − 3 , 1 ) : T = ( − 0.987408 , 0.158192 ) , R = ( 1.187435 , 0.329136 ) P = (-3, 0), Q = (-3, 1):T = (-0.987408 , 0.158192) , R = (1.187435 , 0.329136) P=(−3,0),Q=(−3,1):T=(−0.987408,0.158192),R=(1.187435,0.329136)
P = ( − 10 , 0 ) , Q = ( − 2 , 1 ) : T = ( − 0.959312 , 0.282350 ) , R = ( 0.304214 , 0.321811 ) P = (-10, 0), Q = (-2, 1):T = (-0.959312 , 0.282350) , R = (0.304214 , 0.321811) P=(−10,0),Q=(−2,1):T=(−0.959312,0.282350),R=(0.304214,0.321811)
P = ( − 1024 , 0 ) , Q = ( − 8 , 4 ) : T = ( − 0.970066 , 0.242842 ) , R = ( 7.000894 , 0.244735 ) P = (-1024, 0), Q = (-8, 4):T = (-0.970066 , 0.242842) , R = (7.000894 , 0.244735) P=(−1024,0),Q=(−8,4):T=(−0.970066,0.242842),R=(7.000894,0.244735)
Conclusion
本实验提高精度的主要措施:
-
使用long double 类型;
-
用较多位数来表示 π \pi π;
-
将含有除法的方程交叉相乘,通过乘法代替除法,以减少在求商时的误差;
-
在用二分法解非线性方程时,限制条件是结果的绝对值 ≤ 1 0 − 7 \leq10^{-7} ≤10−7。
但由于本实验的方程较为复杂,含有三角函数、根式、平方等易造成误差放大的因素,暂时还未找到其他较好的减少误差的办法,但还尝试了另一种思路,叙述如下:
设OT的延长线交PQ于R,则由角平分线定理,有
Q
T
P
T
=
Q
R
R
P
=
Q
y
R
y
−
1
\frac{QT}{PT}=\frac{QR}{RP}=\frac{Q_y}{R_y}-1
PTQT=RPQR=RyQy−1
设
T
(
x
,
y
)
,
k
=
Q
y
Q
x
−
P
x
T(x,y),k=\frac{Qy}{Qx-Px}
T(x,y),k=Qx−PxQy,带入上述方程化简得:
Q
y
2
(
k
x
−
y
)
2
+
k
2
P
x
2
y
2
−
2
k
Q
y
P
x
y
(
k
x
−
y
)
k
2
P
x
2
y
2
=
Q
y
2
+
Q
x
2
+
1
−
2
y
Q
y
−
2
x
Q
x
1
+
P
x
2
−
2
x
P
x
\frac{Q_y^2(kx-y)^2+k^2P_x^2y^2-2kQ_yPxy(kx-y)}{k^2Px^2y^2}=\frac{Q_y^2+Q_x^2+1-2yQ_y-2xQ_x}{1+P_x^2-2xP_x}
k2Px2y2Qy2(kx−y)2+k2Px2y2−2kQyPxy(kx−y)=1+Px2−2xPxQy2+Qx2+1−2yQy−2xQx
故只需用for循环遍历或二分法解非线性方程
(
Q
y
2
(
k
x
−
y
)
2
+
k
2
P
x
2
y
2
−
2
k
Q
y
P
x
y
(
k
x
−
y
)
)
(
1
+
P
x
2
−
2
x
P
x
)
−
(
k
2
P
x
2
y
2
)
(
Q
y
2
+
Q
x
2
+
1
−
2
y
Q
y
−
2
x
Q
x
)
=
0
(Q_y^2(kx-y)^2+k^2P_x^2y^2-2kQ_yPxy(kx-y))(1+P_x^2-2xP_x)-(k^2Px^2y^2)(Q_y^2+Q_x^2+1-2yQ_y-2xQ_x)=0
(Qy2(kx−y)2+k2Px2y2−2kQyPxy(kx−y))(1+Px2−2xPx)−(k2Px2y2)(Qy2+Qx2+1−2yQy−2xQx)=0
y
=
1
−
x
2
y=\sqrt{1-x^2}
y=1−x2
由对称性易知
R
x
=
Q
x
T
y
T
x
+
T
x
(
2
T
x
−
Q
x
)
T
y
+
2
T
y
−
2
Q
y
T
x
T
y
+
T
y
T
x
Rx=\frac{\frac{QxTy}{Tx} + \frac{Tx(2Tx - Qx)}{Ty} + 2Ty -2Qy}{\frac{Tx}{Ty}+\frac{Ty}{Tx}}
Rx=TyTx+TxTyTxQxTy+TyTx(2Tx−Qx)+2Ty−2Qy
R
y
=
Q
y
−
T
y
T
x
(
Q
x
−
R
x
)
Ry=Qy-\frac{Ty}{Tx(Qx - Rx)}
Ry=Qy−Tx(Qx−Rx)Ty
但验证结果时发现,上述方法在遇到一些极端情况如
P
=
(
−
1.000001
,
0
)
,
Q
=
(
−
2
,
2
)
P = (-1.000001, 0), Q = (-2, 2)
P=(−1.000001,0),Q=(−2,2)时,中间的运算过程会出现极小的浮点数导致无法继续运算,所以这种思路在细节上仍有待改进。