You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求解使5×5对称Cayley-Menger矩阵奇异的未知边长

求解平面四点Cayley-Menger矩阵奇异时的未知边长(嵌入式C实现)

要解决这个问题,核心是利用平面四点的Cayley-Menger(CM)矩阵行列式为0的约束,推导未知边长的二次方程,再在嵌入式设备上用基础数学函数求解。以下是完整的思路和实现:

核心原理:平面四点的CM矩阵约束

对于平面上的四个点A、B、C、D,对应的5×5 Cayley-Menger矩阵形式如下:

[0, 1, 1, 1, 1]
[1, 0, AB², AC², AD²]
[1, AB², 0, BC², BD²]
[1, AC², BC², 0, CD²]
[1, AD², BD², CD², 0]

四点共面(平面问题必然满足)的充要条件是该矩阵的行列式为0。将未知边长的平方设为变量,展开行列式后会得到一个一元二次方程,解这个方程并筛选正根,就能得到未知边长的可能值(0、1或2个)。

实现步骤

  1. 映射边与索引:将6条边(AB、AC、AD、BC、BD、CD)对应到0-5的索引,方便通用处理。
  2. 构造二次方程:根据未知边的索引,代入已知边的平方值,展开CM行列式得到关于未知边平方的二次方程a*(x²)² + b*(x²) + c = 0。
  3. 求解二次方程:计算判别式判断解的数量,筛选出正的边长平方值,用sqrt()开平方得到有效边长。
  4. 精度处理:嵌入式浮点数计算存在精度误差,需用极小值(如1e-8)判断正负、相等情况。

嵌入式C实现代码

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

// 边索引定义,覆盖所有6条可能的边
#define AB 0
#define AC 1
#define AD 2
#define BC 3
#define BD 4
#define CD 5

/**
 * 求解平面四点CM矩阵奇异时的未知边长
 * @param known_sides 长度为6的数组,未知边位置可填任意值(会被忽略)
 * @param unknown_idx 未知边的索引(0-5,对应AB/AC/AD/BC/BD/CD)
 * @param solutions 存储解的数组,最多容纳2个解
 * @return 有效解的数量(0、1或2)
 */
int solve_cm_unknown_side(const double known_sides[6], int unknown_idx, double solutions[2]) {
    // 提取已知边的平方,未知边的平方先设为0(后续作为变量处理)
    double ab2 = (unknown_idx == AB) ? 0.0 : known_sides[AB] * known_sides[AB];
    double ac2 = (unknown_idx == AC) ? 0.0 : known_sides[AC] * known_sides[AC];
    double ad2 = (unknown_idx == AD) ? 0.0 : known_sides[AD] * known_sides[AD];
    double bc2 = (unknown_idx == BC) ? 0.0 : known_sides[BC] * known_sides[BC];
    double bd2 = (unknown_idx == BD) ? 0.0 : known_sides[BD] * known_sides[BD];
    double cd2 = (unknown_idx == CD) ? 0.0 : known_sides[CD] * known_sides[CD];
    
    double a, b, c;
    // 针对每个未知边,预计算二次方程的系数(代数展开CM行列式得到)
    switch(unknown_idx) {
        case AB:
            a = 16.0;
            b = -8.0*(ac2 + ad2 + bc2 + bd2) + 4.0*cd2;
            c = (ac2 - bc2)*(ad2 - bd2) + cd2*(ac2 + ad2 + bc2 + bd2) - ac2*ad2 - bc2*bd2 - cd2*cd2;
            break;
        case AC:
            a = 16.0;
            b = -8.0*(ab2 + ad2 + bc2 + cd2) + 4.0*bd2;
            c = (ab2 - bc2)*(ad2 - cd2) + bd2*(ab2 + ad2 + bc2 + cd2) - ab2*ad2 - bc2*cd2 - bd2*bd2;
            break;
        case AD:
            a = 16.0;
            b = -8.0*(ab2 + ac2 + bd2 + cd2) + 4.0*bc2;
            c = (ab2 - bd2)*(ac2 - cd2) + bc2*(ab2 + ac2 + bd2 + cd2) - ab2*ac2 - bd2*cd2 - bc2*bc2;
            break;
        case BC:
            a = 16.0;
            b = -8.0*(ab2 + ac2 + bd2 + cd2) + 4.0*ad2;
            c = (ab2 - ac2)*(bd2 - cd2) + ad2*(ab2 + ac2 + bd2 + cd2) - ab2*bd2 - ac2*cd2 - ad2*ad2;
            break;
        case BD:
            a = 16.0;
            b = -8.0*(ab2 + ad2 + bc2 + cd2) + 4.0*ac2;
            c = (ab2 - ad2)*(bc2 - cd2) + ac2*(ab2 + ad2 + bc2 + cd2) - ab2*bc2 - ad2*cd2 - ac2*ac2;
            break;
        case CD:
            a = 16.0;
            b = -8.0*(ac2 + ad2 + bc2 + bd2) + 4.0*ab2;
            c = (ac2 - ad2)*(bc2 - bd2) + ab2*(ac2 + ad2 + bc2 + bd2) - ac2*bc2 - ad2*bd2 - ab2*ab2;
            break;
        default:
            return 0; // 无效索引,直接返回0解
    }
    
    const double eps = 1e-8;
    double delta = b*b - 4*a*c;
    int sol_count = 0;
    
    // 根据判别式判断解的情况
    if (delta < -eps) {
        // 判别式小于0,无实数解
        return 0;
    } else if (fabs(delta) <= eps) {
        // 判别式为0,一个实数解
        double x_sq = -b/(2*a);
        if (x_sq > eps) {
            solutions[sol_count++] = sqrt(x_sq);
        }
    } else {
        // 判别式大于0,两个实数解
        double sqrt_delta = sqrt(delta);
        double x_sq1 = (-b + sqrt_delta)/(2*a);
        double x_sq2 = (-b - sqrt_delta)/(2*a);
        
        if (x_sq1 > eps) {
            solutions[sol_count++] = sqrt(x_sq1);
        }
        if (x_sq2 > eps) {
            solutions[sol_count++] = sqrt(x_sq2);
        }
    }
    
    return sol_count;
}

// 示例使用
int main() {
    // 已知AB=1, AC=1, AD=1, BC=1, BD=1,求解CD的长度
    double known_sides[6] = {1.0, 1.0, 1.0, 1.0, 1.0, 0.0};
    double solutions[2];
    int count = solve_cm_unknown_side(known_sides, CD, solutions);
    
    printf("有效解数量:%d\n", count);
    for (int i = 0; i < count; i++) {
        printf("解%d:%.4f\n", i+1, solutions[i]);
    }
    // 预期输出:2个解,分别为0.0000(退化情况)和1.7321(平面四边形的合理边长)
    return 0;
}

关键说明

  • 预计算系数:二次方程的系数是通过代数展开CM行列式得到的,避免了运行时计算行列式的复杂操作,适合嵌入式设备的性能要求。
  • 精度控制:使用eps(1e-8)处理浮点数的精度问题,避免因计算误差导致的错误判断(比如把极小的负判别式当成无解,或者把负的边长平方当成有效解)。
  • 通用性:支持任意一条边作为未知项,只需传入对应的索引即可。

内容的提问来源于stack exchange,提问作者CMPXCHG8B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 00:52:48