已知两圆圆心坐标及半径求两圆交点

在一个二维平面上给定两个圆的横纵坐标、半径共6个参数,如果两圆不相交,那么输出“NO INTERSECTION”,如果两圆重合,则输出“THE CIRCLES ARE THE SAME”,否则输出交点,相切的话输出1个交点坐标,相交则输出两个,而且要保证横坐标大的在前,如果横坐标一样,则纵坐标大的在前。前两种情况很判断,也很好输出,求交点,无非是解二元二次方程组。然而,这个高中生就能够熟练解决的问题,我写这个程序却花了两个多小时。解这么一个很正常,很容易理解的问题却花费了这么长的时间,总结了一下,原因有二:

选择什么样的公式求解;
一步到位还是分步求解。

第一点,虽然计算机的运算能力很强,但是公式选择不当还是很可能出错、难解、难验证。我一开始只是觉得这是个普通二元二次方程,打算联立消元,后来发现中间过程里的系数很复杂。复杂会怎么样?第一,容易出错,因为公式毕竟还是要人来推导,人的出错率比计算机要高得多得多;第二,不易验证,如果我自己出一些测试数据,计算机求得了答案我自己还不知道是不是正确的。所以后来打消了这个念头,改用圆的参数方程求解。
第二点,一步到位,就是说一次性写出完整的求根公式,然而这样做又是两个字:复杂。所以我还是选择了逐步求解,虽然过程会长一些,演算次数会多一些,但是这毕竟是一个几何题,超时的概率与胡乱打表过的概率相差无几,首先应该保证结果正确,把中间步骤的系数适当化简以免自己都看昏了,调试起来也方便。

现在谈谈这题的求解过程。选择圆的参数方程的好处是方程是一次的,化简方便,虽然是三角函数方程并且既有正弦也有余弦,不过到最后可以很方便地求出来。

输入两圆参数x1,y1,r1,x2,y2,r2,设交点为(x,y),x=r1cosθ+x1,y=r1sinθ+y1,代入另一圆的方程,得到:
(r1cosθ+x1-x2)^2+(r1sinθ+y1-y2)^2=r2^2
展开合并同类项:
2r1(x1-x2)cosθ+2r1(y1-y2)sinθ=r2^2-r1^2-(x1-x2)^2-(y1-y2)^2
现在令:
a=2r1(x1-x2)
b=2r1(y1-y2)
c=r2^2-r1^2-(x1-x2)^2-(y1-y2)^2
那么方程便成为:
acosθ+bsinθ=c
用(1-(cosθ)^2)^(1/2)表示sinθ,令:
p=a^2+b^2
q=-2ac
r=c^2-b^2
便化为一个一元二次方程,解得:
cosθ=(±(q^2-4pr)^(1/2)-q)/(2p)

然而到此为止还没有结束,因为刚才将三角方程转化为二次方程时,等式两边平方了一次,如果直接这样求对应角的正弦值,符号总会为正。为了将纵坐标正确解出,必须变角。那么如何变角?方法当然很多,诱导公式,或者反过头去把方程变一下,代cosθ=(1-(sinθ)^2)^(1/2)解得正弦,这又牵扯到第一条。我在这里选择的是,直接验证所得的解是不是根,如果不是,将纵坐标反号便可以了。最后注意一下两解的横坐标相同的情况,这样要先输出正的再输出负的。

源代码如下(GCC):

#include<stdio.h>
#include<math.h>

const double lim0=1e-9,pi=acos(-1);

int main(){
double x1,y1,r1,x2,y2,r2,d,a,b,c,p,q,r,c1,c2,s1,s2,xs1,ys1,xs2,ys2;

while(scanf("%lf%lf%lf%lf%lf%lf",&x1,&y1,&r1,&x2,&y2,&r2)!=EOF){
   if(x1==x2&&y1==y2&&r1==r2){
    printf("THE CIRCLES ARE THE SAME\n");
   }else{
    d=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    if(d>r1+r2||d<fabs(r1-r2)){
     printf("NO INTERSECTION\n");
    }else if(lim0>fabs(d-r1-r2)||lim0>fabs(d-fabs(r1-r2))){
     a=2.0*r1*(x1-x2);
     b=2.0*r1*(y1-y2);
     c=r2*r2-r1*r1-(x1-x2)*(x1-x2)-(y1-y2)*(y1-y2);
     p=a*a+b*b;
     q=-2.0*a*c;
     c1=-q/p/2.0;
     s1=sqrt(1-c1*c1);
     xs1=r1*c1+x1;
     ys1=r1*s1+y1;
     if(lim0<fabs((xs1-x2)*(xs1-x2)+(ys1-y2)*(ys1-y2)-r2*r2)){
      ys1=-r1*s1+y1;
     }printf("(%.3lf %.3lf)\n",xs1,ys1);
    }else{
     a=2.0*r1*(x1-x2);
     b=2.0*r1*(y1-y2);
     c=r2*r2-r1*r1-(x1-x2)*(x1-x2)-(y1-y2)*(y1-y2);
     p=a*a+b*b;
     q=-2.0*a*c;
     r=c*c-b*b;
     c1=(sqrt(q*q-4.0*p*r)-q)/p/2.0;
     c2=(-sqrt(q*q-4.0*p*r)-q)/p/2.0;
     s1=sqrt(1-c1*c1);
     s2=sqrt(1-c2*c2);
     xs1=r1*c1+x1;
     xs2=r1*c2+x1;
     ys1=r1*s1+y1;
     ys2=r1*s2+y1;
     if(lim0<fabs((xs1-x2)*(xs1-x2)+(ys1-y2)*(ys1-y2)-r2*r2)){
      ys1=-r1*s1+y1;
     }if(lim0<fabs((xs2-x2)*(xs2-x2)+(ys2-y2)*(ys2-y2)-r2*r2)){
      ys2=-r1*s2+y1;
     }if(lim0>fabs(ys1-ys2)&&lim0>fabs(xs1-xs2)){
      if(ys1>y1){
       ys2=2.0*y1-ys1;
      }else{
       ys1=2.0*y1-ys1;
      }
     }printf("(%.3lf %.3lf) (%.3lf %.3lf)\n",xs1,ys1,xs2,ys2);
    }
   }
}return 0;
}
//由于sqrt(q*q-4.0*p*r)总是非负的,所以可以确保xs1>=xs2

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值