应用程序开发总结(10)--存在完美的数学计算

第12章  存在完美的数学计算

   这章写出来应该不是那么容易,因为题目是一个命题,是真是假超越普通人讨论的范围。取这个题目,是我想表达出,关于程序中的数值计算,存在一些让人惊奇的方法。避免使用类似“打补丁”的方式来做数学,因为或许存在一种数值计算方法,能够简简单单的把你一大堆的if,else的语句全部包含。由于数学是广博的,难以笼统的表达出来,下面我就以几个简单的例子来说明哪些数值计算方式体现出数学的完美。
    

      12.1 矩阵计算

        无论是大数据计算,还是空间计算。矩阵是这些计算最好的表达方式。矩阵将复杂的世界划分为多个维度(虽然会有同学认为,世界是可以理解一点的,而矩阵不是)。我经常面对的是基于一个点,然后构建一个局部坐标系。如下面代码所示:
 /// <summary>
        /// 构建基于空间位置position所在的点的局部坐标系,不包含位置偏移
        /// <para>Z轴Position向上</para>
        /// <para>Y轴指向正北</para>
        /// <para>X轴指向东方</para>
        /// </summary>
        /// <param name="position"></param>
        /// <returns></returns>
        public static Matrix BuildPositionRelatedMatrix(CoordinatePosition position)
        {
            Vector3 zAxis = position.Vector3;
            zAxis.Normalize();
            Vector3 yAxis = new Vector3(-zAxis.X, -zAxis.Y, 0);
            yAxis.Normalize();
            Vector3 xAxis = Vector3.Cross(yAxis, zAxis);
            xAxis.Normalize();
            yAxis = Vector3.Cross(zAxis, xAxis);
            yAxis.Normalize();

            Matrix m = new Matrix();
            m.M11 = xAxis.X; m.M12 = xAxis.Y; m.M13 = xAxis.Z; m.M14 = 0;
            m.M21 = yAxis.X; m.M22 = yAxis.Y; m.M23 = yAxis.Z; m.M24 = 0;
            m.M31 = zAxis.X; m.M32 = zAxis.Y; m.M33 = zAxis.Z; m.M34 = 0;
            m.M41 = 0; m.M42 = 0; m.M43 = 0; m.M44 = 1;

            return m;
        }
     上面的代码很简洁,一不小心就看完了。它的作用是虚构一个局部坐标系,基于局部坐标系的计算比基于原来的坐标系更容易。如果你没有学过四元数,那么这个矩阵计算可以间接的计算四元数的旋转方法。
     矩阵运算的难度不在于计算的过程,而在于理解如何使用矩阵计算和什么时候可以可以使用矩阵。有些同学或许可以通过普通函数等方式来做一些矩阵计算(比如向量旋转,既可以通过三角函数计算,也可以使用矩阵的方式)。但是使用普通的方式一般需要绕一个长长的思维转换,有时候不得不把不能实现的过程删除。

     12.2 概率思维

   概率计算我用的少,但是概率思维确实帮助很大。一些同学没有量化的概念,只有可能发生就认为就值得的去努力。现在有很多软件,满满的铺满的按钮,提供各式各样的功能,但它知道多少功能普通人会用到。
    概率函数大家用的也多,我有时候会根据一些对象构建它的hash值,hash值是存在重复的,但是重复率很低很低。即使重复了,对系统的基本要求也是不影响的。但是一些同学会揪着这个“可能”不放,从而影响开发思想的统一。
     使用概率思想的地方,可能需要给系统打一些“补丁”,比如上面可能出现的重复,这些补丁是轻量级的。

     12.3 平滑计算

    我们都知道,计算机对数值存在精度问题,所以计算过程中很容易出现精度误差。应该避免的是很大的数或者很小的数进行乘除等运算。还有避免判断小数的数值是否相同。比如,我们通常判断两个线段是否相交的过程是:
  1. 分别建立线段A和线段B的直线方程
  2. 计算直线相交的点P。
  3. 判断P是否在线段A内部,判断P是否在线段B内部
     上述的方法存在的问题是最后一点。因为存在数值等于判断,如果步骤1,2存在精度截断,那么3的判断很可能不准确。
     而我推荐的方法是判断两点是否在一条直线的同一边的方式,也就是线段A的两个点都在直线B的同一边,同时线段B的两个点都在直线B的同一边。
  1. 构建线段A的直线
  2. 对B的两个顶点,判断是否在直线A的同一侧,如果不是,退出
  3. 同理对A的两个顶点进行处理。
       部分代码如下:
           double A, B, C, value1, value2;
            A = line1EP.Y - line1SP.Y;
            B = line1SP.X - line1EP.X;
            C = -line1SP.X * A - line1SP.Y * B;
            value1 = A * line2SP.X + B * line2SP.Y + C;
            value2 = A * line2EP.X + B * line2EP.Y + C;
            if (value1 > 0 && value2 > 0)
            {
                return false;
            }
            if (value1 < 0 && value2 < 0)
            {
                return false;
            }
    上面计算虽然可能会有截断,但是A,B的值相对会很小,从而整体截断误差小,从而保证更高的准确率。

猜你喜欢

转载自blog.csdn.net/htsitr2/article/details/39156181
今日推荐