c语言-求两圆交点

    在一个二维平面上给定两个圆的圆心横纵坐标、半径共6个参数, 求交点. 这个问题无非是解二元二次方程组.普通二元二次方程联立消元求解的困难在于, 中间过程里的系数会变得非常复杂, 从而导致容易出错—因为公式毕竟还是要人来推导,人的出错率比计算机要高得多得多—改用圆的参数方程求解, 可以在显著地减轻这个负担.
现在谈谈这问题的求解过程. 选择圆的参数方程的好处是方程是一次的, 化简方便, 虽然是三角函数方程并且既有正弦也有余弦, 不过到最后可以很方便地求出来.

(下面分析中x^y表示x的y次方)
大家还记得圆的参数方程吧, 圆心 (x0, y0), 半径为 r 的圆的参数方程是:
    x = r * cosθ + x0
    y = r * sinθ + y0
假设现在两圆参数x1, y1, r1, x2, y2, r2(这些分别表示, 咳, 有谁看不出来它们分别表示什么吗?), 设交点为 (x, y), 代入其中一个圆中的参数方程有
    x = r1 * cosθ + x1 且 y = r1 * sinθ + y1
代入另一圆的标准方程, 得到
    (r1 * cosθ + x1 - x2)^2 + (r1 * sinθ + y1 - y2)^2 = r2^2
是的, 看起来有关于正余弦二次项, 不过不要惊慌, 展开合并同类项之后, 正好这两项会合并成常数:
左边 = (r1 * cosθ)^2 + (r1 * sinθ)^2 + 2 * r1 * (x1 - x2) * cosθ + 2 * r1 * (y1 - y2) * sinθ
      = r2^2 - (x1 - x2)^2 - (y1 - y2)^2 = 右边
这样就好办了, 把 r1^2 转移到等式右边, 令:
   a = 2 * r1 * (x1 - x2)
    b = 2 * r1 * (y1 - y2)
    c = r2^2 - r1^2 - (x1 - x2)^2 - (y1 - y2)^2
那么方程便成为:
    a * cosθ + b * sinθ = c
用 (1 - (cosθ)^2)^(1 / 2) 表示sinθ, 令:
    p = a^2 + b^2
    q = -2 * a * c
    r = c^2 - b^2
便化为一个一元二次方程, 解得:
    cosθ = (±(q^2 - 4 * p * r)^(1 / 2) - q) / (2 * p)

    然而到此为止还没有结束, 因为刚才将三角方程转化为二次方程时, 等式两边平方了一次, 如果直接这样求对应角的正弦值, 符号总会为正.为了将纵坐标正确解出, 必须变角. 那么如何变角?方法当然很多, 诱导公式, 或者反过头去把方程变一下, 以正弦作为未知数,但是这些方法都比较复杂. 在这里可以选择另一种方案, 那就是用验证代替求解: 验证所得的解是不是根, 如果不是, 将纵坐标反号便可以了.最后注意一下两解的横坐标相同的情况, 这样要先输出正的再输出负的.

#include<math.h>  

struct point_t {  
    double x, y;  
};  

struct circle_t {  
    struct point_t center;  
    double r;  
};  

int double_equals(double const a, double const b)  
{  
    static const double ZERO = 1e-9;  
    return fabs(a - b) < ZERO;  
}  

double distance_sqr(struct point_t const* a, struct point_t const* b)  
{  
    return (a->x - b->x) * (a->x - b->x) + (a->y - b->y) * (a->y - b->y);  
}  

double distance(struct point_t const* a, struct point_t const* b)  
{  
    return sqrt(distance_sqr(a, b));  
}  

int insect(struct circle_t circles[], struct point_t points[])  
{  
    double d, a, b, c, p, q, r;  
    double cos_value[2], sin_value[2];  
    if (double_equals(circles[0].center.x, circles[1].center.x)  
        && double_equals(circles[0].center.y, circles[1].center.y)  
        && double_equals(circles[0].r, circles[1].r)) {  
        return -1;  
    }  

    d = distance(&circles[0].center, &circles[1].center);  
    if (d > circles[0].r + circles[1].r  
        || d < fabs(circles[0].r - circles[1].r)) {  
        return 0;  
    }  

    a = 2.0 * circles[0].r * (circles[0].center.x - circles[1].center.x);  
    b = 2.0 * circles[0].r * (circles[0].center.y - circles[1].center.y);  
    c = circles[1].r * circles[1].r - circles[0].r * circles[0].r  
        - distance_sqr(&circles[0].center, &circles[1].center);  
    p = a * a + b * b;  
    q = -2.0 * a * c;  
    if (double_equals(d, circles[0].r + circles[1].r)  
        || double_equals(d, fabs(circles[0].r - circles[1].r))) {  
        cos_value[0] = -q / p / 2.0;  
        sin_value[0] = sqrt(1 - cos_value[0] * cos_value[0]);  

        points[0].x = circles[0].r * cos_value[0] + circles[0].center.x;  
        points[0].y = circles[0].r * sin_value[0] + circles[0].center.y;  

        if (!double_equals(distance_sqr(&points[0], &circles[1].center),  
                           circles[1].r * circles[1].r)) {  
            points[0].y = circles[0].center.y - circles[0].r * sin_value[0];  
        }  
        return 1;  
    }  

    r = c * c - b * b;  
    cos_value[0] = (sqrt(q * q - 4.0 * p * r) - q) / p / 2.0;  
    cos_value[1] = (-sqrt(q * q - 4.0 * p * r) - q) / p / 2.0;  
    sin_value[0] = sqrt(1 - cos_value[0] * cos_value[0]);  
    sin_value[1] = sqrt(1 - cos_value[1] * cos_value[1]);  

    points[0].x = circles[0].r * cos_value[0] + circles[0].center.x;  
    points[1].x = circles[0].r * cos_value[1] + circles[0].center.x;  
    points[0].y = circles[0].r * sin_value[0] + circles[0].center.y;  
    points[1].y = circles[0].r * sin_value[1] + circles[0].center.y;  

    if (!double_equals(distance_sqr(&points[0], &circles[1].center),  
                       circles[1].r * circles[1].r)) {  
        points[0].y = circles[0].center.y - circles[0].r * sin_value[0];  
    }  
    if (!double_equals(distance_sqr(&points[1], &circles[1].center),  
                       circles[1].r * circles[1].r)) {  
        points[1].y = circles[0].center.y - circles[0].r * sin_value[1];  
    }  
    if (double_equals(points[0].y, points[1].y)  
        && double_equals(points[0].x, points[1].x)) {  
        if (points[0].y > 0) {  
            points[1].y = -points[1].y;  
        } else {  
            points[0].y = -points[0].y;  
        }  
    }  
    return 2;  
}  

int main()  
{  
    struct circle_t circles[2];  
    struct point_t points[2];  
    printf("请输入两圆x,y,半径(以逗号分开):");  
    while (EOF != scanf("%lf,%lf,%lf,%lf,%lf,%lf",  
                   &circles[0].center.x, &circles[0].center.y, &circles[0].r,  
                   &circles[1].center.x, &circles[1].center.y, &circles[1].r)) {  
        switch (insect(circles, points)) {  
            case -1:  
                printf("THE CIRCLES ARE THE SAME/n");  
                break;  
            case 0:  
                printf("NO INTERSECTION/n");  
                break;  
            case 1:  
                printf("(%.3lf %.3lf)\n", points[0].x, points[0].y);  
                break;  
            case 2:  
                printf("(%.3lf %.3lf) (%.3lf %.3lf)\n",  
                       points[0].x, points[0].y,  
                       points[1].x, points[1].y);  
        }  
    }  
    return 0;  
}  
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
本程序是两圆相交交点,输入信息为两圆的圆心坐标和半径值。如有更好的算法,欢迎交流!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值