zoukankan      html  css  js  c++  java
  • 【数学模型】拟合平面

    https://www.cnblogs.com/zhangli07/p/12013561.html

    二、最小二乘面拟合

    对空间中的一系列散点,寻求一个近似平面,与线性最小二乘一样,只是变换了拟合方程:

    使用平面的一般方程:

    Ax + By + CZ + D = 0

    可以令平面方程为:

     由最小二乘法知:

     同样分别取 a0,a1,a2的偏导数:

     即是:

     换算为矩阵形式有:

     可以直接通过克拉默法则求出a0,a1,a2的行列式表达式,有:

     c++实现(gDaterm3() 为自定义的三阶行列式计算函数):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    bool gFittingPlane(double *x, double *y, double *z, int n, double &a, double &b, double &c)
    {
        int i;
        double x1, x2, y1, y2, z1, xz, yz, xy, r;
     
        x1 = x2 = y1 = y2 = z1 = xz = yz = xy = 0;
        for(i=0; i<n; i++)
        {
            x1 += x[i];
            x2 += x[i]*x[i];
            xz += x[i]*z[i];
     
            y1 += y[i];
            y2 += y[i]*y[i];
            yz += y[i]*z[i];
     
            z1 += z[i];
            xy += x[i]*y[i];
        }
     
        r = gDeterm3(x2, xy, x1, xy, y2, y1, x1, y1, n);
        if(IS_ZERO(r)) return false;
     
        a = gDeterm3(xz, xy, x1, yz, y2, y1, z1, y1, n) / r;
        b = gDeterm3(x2, xz, x1, xy, yz, y1, x1, z1, n) / r;
        c = gDeterm3(x2, xy, xz, xy, y2, yz, x1, y1, z1) / r;
     
        return true;
    }

    https://zhidao.baidu.com/question/293032018.html

    liwenwei9909 
    2011-07-18
    解:2113
    设有n个数据点Pi(xi, yi, zi)。
    假设平面方程5261为:a*x+b*y+c*z+d=0,其中a、b、c、d为待定系数4102a、b、c不能同1653时为0。
    显然,a*x+b*y+c*z+d=0与
    k*a*x+k*b*y+k*c*z+k*d=0(k≠0)
    表示同一个平面。故,如d不为0,可通过把方程两边同除以d,把常数项化为1;但d=0时,情况稍微复杂一点。

    现在说明大致思路,为讨论方便,开始时暂不假设d=1或0。
    设拟合平面的方程为∏:a*x+b*y+c*z+d=0。
    数据点Pi到平面a*x+b*y+c*z+d=0的距离设为di,
    则di^2=(a*xi+b*yi+c*zi+d)^2/(a^2+b^2+c^2),
    令L=∑di^2 (i=1, ... , n),为目标函数,现欲使L最小。
    L可以看成是关于(a, b, c, d)的函数((xi, yi, zi)均已知),
    L取最小值的一个必要(非充分)条件是:
    ∂L/∂a=0, ∂L/∂b=0, ∂L/∂c=0, ∂L/∂d=0,

    ∂L/∂a=∑2*xi*(a*xi+b*yi+c*zi+d)/(a^2+b^2+c^2) (i=1, ... , n)
    =A1*a+B1*b+C1*c+D1*d,
    其中,
    A1=2/(a^2+b^2+c^2)*(∑xi^2)(i=1, ... , n),
    B1=2/(a^2+b^2+c^2)*(∑xi*yi)(i=1, ... , n),
    C1=2/(a^2+b^2+c^2)*(∑xi*zi)(i=1, ... , n),
    D1=2/(a^2+b^2+c^2)*(∑xi)(i=1, ... , n),
    同理,
    ∂L/∂b=A2*a+B2*b+C2*c+D2*d,
    ∂L/∂c=A3*a+B3*b+C3*c+D3*d,
    其中,
    A2=2/(a^2+b^2+c^2)*(∑yi*xi)(i=1, ... , n),
    B2=2/(a^2+b^2+c^2)*(∑yi^2)(i=1, ... , n),
    C2=2/(a^2+b^2+c^2)*(∑yi*zi)(i=1, ... , n),
    D2=2/(a^2+b^2+c^2)*(∑yi)(i=1, ... , n),

    A3=2/(a^2+b^2+c^2)*(∑zi*xi)(i=1, ... , n),
    B3=2/(a^2+b^2+c^2)*(∑zi*yi)(i=1, ... , n),
    C3=2/(a^2+b^2+c^2)*(∑zi^2)(i=1, ... , n),
    D3=2/(a^2+b^2+c^2)*(∑zi)(i=1, ... , n),

    ∂L/∂d=∑2*(a*xi+b*yi+c*zi+d)/(a^2+b^2+c^2) (i=1, ... , n)
    =D1*a+D2*b+D3*c+D4*d,
    其中,D4=2n/(a^2+b^2+c^2)。

    于是有方程组:
    A1*a+B1*b+C1*c+D1*d=0,
    A2*a+B2*b+C2*c+D2*d=0,
    A3*a+B3*b+C3*c+D3*d=0,
    D1*a+D2*b+D3*c+D4*d=0,
    解此方程组即可。具体如何解,可参考计算方法的书,上面有详细说明。
    数值方法解线性方程组最好不要用行列式来算。

    在如下网址有一篇介绍文档
    http://wenku.baidu.com/view/c9d0713710661ed9ac51f305.html
    其中第一步就有遗漏,而且后面还在用行列式来计算,不是太好。

    --------------------------------------------------------------------------------------------

    通过第一种方法的偏导,启发,理解了第二种方法。但第二种方法程序解方程计算出来的结果有最大常数 × 10的负13次方的误差(C# double乘和除之后 造成误差放大)。第一种方法求出来的平面是错误的结果,但可根据第一种方法的矩阵求解思路,解第二种方法的方程。

    分别设 d 不等于零时,取d=1024;

    d不等于零时,试取 a = 1024, 或 b =1024 或 c = 1024。只要求得 a b c d 不同时等于零即可。

    -------------------------------------------------------------------------------------------------

    验证平面方法,取3点,计算出平面,把a b c d代入用于拟合平面的方程,看是否成立,若成立,则可通过解方程拟合平面。

    经测试,第一种方法代入a,b,c,d后方程组不成立,故是错误的方程组。

    第二种方法代入a,b,c,d后方程组成立,可解方程拟合平面。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using FunctionalProgramming;
    using FunctionalProgramming.Extension;
    using MathPrograming.Calculate;
    
    namespace MathPrograming.MathSpace
    {
        /// <summary>
        /// 平面ax+by+cz+d=0
        /// </summary>
        public class MathPlane
        {
            //public:
    
            public double A { get; set; }
            public double B { get; set; }
            public double C { get; set; }
            public double D { get; set; }
    
            public double GetDistanceToPoint(double x, double y, double z)
            {
                return Math.Abs(A * x + B * y + C * z + D) / Math.Sqrt(A * A + B * B + C * C);
            }
    
    
            /// <summary>
            /// 获取平面的法向量
            /// </summary>
            /// <returns></returns>
            public FuncNode<MathSpatialVector> GetNormalVector()
            {
                return new MathSpatialVector { X = this.A, Y = this.B, Z = this.C };
            }
    
    
    
            //pubilc static:
    
            /// <summary>
            ///  拟合平面  最小二乘法 偏导 方程求解(存在很小的误差)
            /// </summary>
            /// <param unknown_name="points"></param>
            /// <returns></returns>
            public static FuncNode<MathPlane> FittingPlane(MathSpatialPoint[] points)
            {
                // 最小二乘法 偏导 方程求解
                // a∑x2 + b∑xy + c∑xz + d∑x = 0;
                // a∑xy + b∑y2 + c∑yz + d∑y = 0;
                // a∑xz + b∑yz + c∑z2 + d∑z = 0;
                // a∑x + b∑y + c∑z + d∑1 = 0;
    
    
                double x = 0, x2 = 0, xy = 0, xz = 0, y = 0, y2 = 0, yz = 0, z = 0, z2 = 0, n = points.Length;
    
                foreach (MathSpatialPoint point1 in points)
                {
                    x += point1.X;
                    x2 += point1.X * point1.X;
                    xy += point1.X * point1.Y;
                    xz += point1.X * point1.Z;
    
                    y += point1.Y;
                    y2 += point1.Y * point1.Y;
                    yz += point1.Y * point1.Z;
    
                    z += point1.Z;
                    z2 += point1.Z * point1.Z;
                }
    
    
                #region
                // 消元法,假设 d为零和不为零的情况求解 2020-8-25 16:30:48
                // 编写写四元一次方程组算法 解代码 2020-8-20 17:19:45
                var eqs = new List<LinearEquation>();
    
    
                
    
                return LinearEquation.Create(new ExpressionItem[] { new Unknown("a", x2), new Unknown("b", xy), new Unknown("c", xz), new Unknown("d", x), }, new ExpressionItem[] { new Number(0) })
                     .Bind(eq1 =>
                     {
                         eqs.Add(eq1);
                         return LinearEquation.Create(new ExpressionItem[] { new Unknown("a", xy), new Unknown("b", y2), new Unknown("c", yz), new Unknown("d", y), }, new ExpressionItem[] { new Number(0) });
                     }).Bind(eq1 =>
                     {
                         eqs.Add(eq1);
                         return LinearEquation.Create(new ExpressionItem[] { new Unknown("a", xz), new Unknown("b", yz), new Unknown("c", z2), new Unknown("d", z), }, new ExpressionItem[] { new Number(0) });
                     }).Bind(eq1 =>
                     {
                         eqs.Add(eq1);
                         return LinearEquation.Create(new ExpressionItem[] { new Unknown("a", x), new Unknown("b", y), new Unknown("c", z), new Unknown("d", n), }, new ExpressionItem[] { new Number(0) });
                     })
                     .Bind(eq1 =>
                     {
                         eqs.Add(eq1);
                         return SolvePlane(eqs.ToArray());
                         // # 测试得,4个组合的结果算出来的平面误差很小
                         //根据4个方程,拟合可能的平面。 2020-8-26 9:23:44
                         //return Combination.Of(eqs.ToArray(), 3)
                         //    .Bind<MathPlane[]>(combination_arr => 
                         //        {
                         //            if (combination_arr.Length == 0) { return "拟合平面失败,获取平面方程的组合失败。".AsFuncNodeError(); }
    
                         //            List<MathPlane> plane_s = new List<MathPlane>();
                         //            for (int i = 0; i < combination_arr.Length; i++)
                         //            {
                         //                SolvePlane(combination_arr[i]).ForEach(plane_s.Add);
                         //            }
                         //            if (plane_s.Count == 0) { return "拟合平面失败。".AsFuncNodeError(); }
                         //            return plane_s.ToArray();
                         //        });
                         
                     });
    
                #endregion
    
            }
    
            
            /// <summary>
            ///  拟合平面  最小二乘法 偏导 矩阵求解
            /// </summary>
            /// <param unknown_name="points"></param>
            /// <returns></returns>
            /// todo #z-extend  试用矩阵(克拉默)法则求平面; 2020-8-26 10:13:36
            //public static FuncNode<MathPlane> FittingPlane2(MathSpatialPoint[] points)
            //{
    
    
            //    // 最小二乘法 偏导 方程求解
            //    // a∑x2 + b∑xy + c∑xz + d∑x = 0;
            //    // a∑xy + b∑y2 + c∑yz + d∑y = 0;
            //    // a∑xz + b∑yz + c∑z2 + d∑z = 0;
            //    // a∑x + b∑y + c∑z + d∑1 = 0;
    
    
            //    double x = 0, x2 = 0, xy = 0, xz = 0, y = 0, y2 = 0, yz = 0, z = 0, z2 = 0, n = points.Length;
    
            //    foreach (MathSpatialPoint point1 in points)
            //    {
            //        x += point1.X;
            //        x2 += point1.X * point1.X;
            //        xy += point1.X * point1.Y;
            //        xz += point1.X * point1.Z;
    
            //        y += point1.Y;
            //        y2 += point1.Y * point1.Y;
            //        yz += point1.Y * point1.Z;
    
            //        z += point1.Z;
            //        z2 += point1.Z * point1.Z;
            //    }
    
    
            //    #region
            //    var eqs = new List<LinearEquation>();
    
            //    //假设 d != 0; 取 d = 1024;
            //    double r = 
            //}
    
    
            /// <summary>
            /// 已知3点坐标,求平面ax+by+cz+d=0; 
            /// </summary>
            /// <param unknown_name="p1"></param>
            /// <param unknown_name="p2"></param>
            /// <param unknown_name="p3"></param>
            /// <returns></returns>
            public static FuncNode<MathPlane> GetPanel(MathSpatialPoint p1, MathSpatialPoint p2, MathSpatialPoint p3)
            {
                MathPlane plane = new MathPlane();
                plane.A = ((p2.Y - p1.Y) * (p3.Z - p1.Z) - (p2.Z - p1.Z) * (p3.Y - p1.Y));
    
                plane.B = ((p2.Z - p1.Z) * (p3.X - p1.X) - (p2.X - p1.X) * (p3.Z - p1.Z));
    
                plane.C = ((p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X));
    
                plane.D = (0 - (plane.A * p1.X + plane.B * p1.Y + plane.C * p1.Z));
    
                return plane;
            }
    
    
    
            //private static:
            private static FuncNode<MathPlane> SolvePlane(LinearEquation[] eq_arr)
            {
                double a = 0, b = 0, c = 0, d = 0;
                Dictionary<string, double> dict_vars = new Dictionary<string, double>(); //用于方程的未知数赋默认值 2020-8-25 9:00:57
    
                //假设D 不等于零 ,令A = a/d, B = b/d, C = c/d, D = 1;
                dict_vars.Add("d", 1024);
                return LinearEquationEvaluator.Solve(eq_arr, dict_vars)  //解方程组 2020-8-24 10:33:50
                    .Bind(dic1 =>
                    {
                        a = dic1["a"]; b = dic1["b"]; c = dic1["c"]; d = dic1["d"];
    
                        if (a == 0 && b == 0 && c == 0 && d == 0)
                        {
                            //假设D 等于零 ,令A = a, B = b, C = c, D = 0;
                            dict_vars.Clear();
                            dict_vars.Add("d", 0); //若d为零,故设 a 不为零。 2020-8-26 8:43:33
                            dict_vars.Add("a", 1024);
                            return LinearEquationEvaluator.Solve(eq_arr.ToArray(), dict_vars);
                        }
                        return dic1;
                    }).Bind(dic1 =>
                    {
                        a = dic1["a"]; b = dic1["b"]; c = dic1["c"]; d = dic1["d"];
    
                        if (a == 0 && b == 0 && c == 0 && d == 0)
                        {
                            dict_vars.Clear();
                            dict_vars.Add("d", 0); //若d为零,故设 b 不为零。 2020-8-26 8:43:33
                            dict_vars.Add("b", 1024);
                            return LinearEquationEvaluator.Solve(eq_arr.ToArray(), dict_vars);
                        }
                        return dic1;
    
                    }).Bind(dic1 =>
                    {
                        a = dic1["a"]; b = dic1["b"]; c = dic1["c"]; d = dic1["d"];
    
                        if (a == 0 && b == 0 && c == 0 && d == 0)
                        {
                            dict_vars.Clear();
                            dict_vars.Add("d", 0); //若d为零,故设 c 不为零。 2020-8-26 8:43:33
                            dict_vars.Add("c", 1024);
                            return LinearEquationEvaluator.Solve(eq_arr.ToArray(), dict_vars);
                        }
                        return dic1;
                    })
                    .Match(dic1 =>
                    {
                        a = dic1["a"]; b = dic1["b"]; c = dic1["c"]; d = dic1["d"];
    
                        if (a == 0 && b == 0 && c == 0 && d == 0)
                        {
                            return "拟合平面失败, ax + by + cz +d = 0 中 a, b, c, d 不能同时为零".AsFuncNodeError();
                        }
                        return new MathPlane { A = a, B = b, C = c, D = d, };
                    }, err => err.ConvertTo<MathPlane>());
            }
    
        }
    }

    测试代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using MathPrograming.Calculate;
    using MathPrograming.MathSpace;
    
    namespace test
    {
        class Program
        {
            static void Main(string[] args)
            {
    
                MathSpatialPoint v1 = new MathSpatialPoint { X = 2, Y = 31, Z = 1 };
                MathSpatialPoint v2 = new MathSpatialPoint { X = 7, Y = 12, Z = 1 };
                MathSpatialPoint v3 = new MathSpatialPoint { X = 71, Y = 1, Z = 82 };
    
                
                var points = new MathSpatialPoint[] { v1, v2, v3, };
    
                double x = 0, x2 = 0, xy = 0, xz = 0, y = 0, y2 = 0, yz = 0, z = 0, z2 = 0, n = points.Length;
    
                foreach (MathSpatialPoint point1 in points)
                {
                    x += point1.X;
                    x2 += point1.X * point1.X;
                    xy += point1.X * point1.Y;
                    xz += point1.X * point1.Z;
    
                    y += point1.Y;
                    y2 += point1.Y * point1.Y;
                    yz += point1.Y * point1.Z;
    
                    z += point1.Z;
                    z2 += point1.Z * point1.Z;
                }
    
    
                Action<MathPlane> show = plane1 =>
                {
    
                    Console.WriteLine("{3}*{0} + {4}*{1} + {5}*{2} + {6} = 0 {7}",
                        v1.X, v1.Y, v1.Z,
                        plane1.A, plane1.B, plane1.C, plane1.D,
                        ((v1.X * plane1.A + v1.Y * plane1.B + v1.Z * plane1.C + plane1.D) == 0).ToString());
                    Console.WriteLine("{3}*{0} + {4}*{1} + {5}*{2} + {6} = 0 {7}",
                        v2.X, v2.Y, v2.Z,
                        plane1.A, plane1.B, plane1.C, plane1.D,
                        ((v2.X * plane1.A + v2.Y * plane1.B + v2.Z * plane1.C + plane1.D) == 0).ToString());
                    Console.WriteLine("{3}*{0} + {4}*{1} + {5}*{2} + {6} = 0 {7}",
                        v3.X, v3.Y, v3.Z,
                        plane1.A, plane1.B, plane1.C, plane1.D,
                        ((v3.X * plane1.A + v3.Y * plane1.B + v3.Z * plane1.C + plane1.D) == 0).ToString());
    
    
                    Console.WriteLine("a = {0}, b = {1}, c = {2}, d = {3}", plane1.A, plane1.B, plane1.C, plane1.D);
    
                    Action<double, double, double, double, double, double, double, double> write_line = (_a, _b, _c, _d, _x, _y, _z, _n) =>
                    {
                        Console.WriteLine("{4}*{0} + {5}*{1} + {6}*{2} + {7}*{3} = 0  {8}", _x, _y, _z, _n, _a, _b, _c, _d, ((_x * _a + _y * _b + _z * _c + _n * _d) == 0).ToString());
                    };
                    
                    write_line(plane1.A, plane1.B, plane1.C, plane1.D, x2, xy, xz, x);
                    write_line(plane1.A, plane1.B, plane1.C, plane1.D, xy, y2, yz, y);
                    write_line(plane1.A, plane1.B, plane1.C, plane1.D, xz, yz, z2, z);
                    write_line(plane1.A, plane1.B, plane1.C, plane1.D, x, y, z, n);
                };
    
                //3 点构成平面
                MathPlane.GetPanel(v1, v2, v3).ForEach(show);
    
    #if DEBUG
    #endif
    
                //多点拟合平面
                MathPlane.FittingPlane(points).Match(res =>
                {
    
                    Console.WriteLine();
                    Console.WriteLine();
                    show(res);
                }, err => Console.WriteLine(err));
    
                Console.ReadKey();
            }
    
        }
    
    
    
    }
  • 相关阅读:
    Linux进程管理
    GitHub
    MySQL存储过程
    MySQL自定义函数
    MySQL运算符和内置函数
    js类型检测
    防止SQL注入的方法
    PDO数据库抽象层
    PHP操作MySQL的常用函数
    第二周
  • 原文地址:https://www.cnblogs.com/GothicLolita/p/13563876.html
Copyright © 2011-2022 走看看