12.1 三维点数据快速索引
class
class Vector3{ public double X,Y,Z;}
class Vector3Ex{ public Vector3 Vect;public int Index;}
class Vector3ExList{
public List<Vector3Ex> Items=new List<Vector3Ex>();
public void Add(Vector3 v,int index){
Items.Add(new Vector3Ex(){vect=v,Index=index};);
}
}
class Vector3XCollection{
public Vector3ExList[] Items=new Vector3ExList[100];
public void Add(int key,Vector3 v,int index){
Items[key].Add(new Vector3Ex(v,index));
}
}
public class Vector3IndexHelper{
private List<Vector3XCollection> m_keys=new List<Vector3XCollection>();
public Vector3IndexHelper(List<Vector3> items){
for(var i=0;i<3;i++){
m_keys[i]=new Vector3XCollection();
}
int index=0;
foreach(var item in items){
var keys=BuildKeys(item);
for(var i=0;i<keys.length;i++){
m_keys[i].Add(keys[i],item,index);
}
}
index++;0
}
public int GetIndex(Vector3 v){
var keys=BuildKeys(v);
int minCount=int.MaxValue;
int selIndex=0;
for(var i=0;i<3;i++){
if(minCount>m_keys[keys[i].Items.Count){
selIndex=i;
}
}
for(var j=0;j<m_keys[i].Items.Count;j++){
if(m_keys[i].Items[j].Vect==v){
return j;
}
}
return 0;
}
private int[] BuildKeys(Vector3 v){
var tx=Math.Floor(v.x);
var ty=Math.Floor(v.y);
var tz=Math.Floor(v.z);
var k1=(int)(tx+ty+tz)%100;
var k2=(int)(tx*tx+ty*ty+tz*tz)%100;
var k3=(int)(tx*ty+*tz)%100;
return new int[]{k1,k2,k3};
}
}
12.2 矩阵计算
无论是大数据计算,还是空间计算。矩阵是这些计算最好的表达方式。矩阵将复杂的世界划分为多个维度(虽然会有同学认为,世界是可以理解一点的,而矩阵不是)。我经常面对的是基于一个点,然后构建一个局部坐标系。如下面代码所示:
/// <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 平滑计算
我们都知道,计算机对数值存在精度问题,所以计算过程中很容易出现精度误差。应该避免的是很大的数或者很小的数进行乘除等运算。还有避免判断小数的数值是否相同。比如,我们通常判断两个线段是否相交的过程是:
- 分别建立线段A和线段B的直线方程
- 计算直线相交的点P。
- 判断P是否在线段A内部,判断P是否在线段B内部
上述的方法存在的问题是最后一点。因为存在数值等于判断,如果步骤1,2存在精度截断,那么3的判断很可能不准确。
而我推荐的方法是判断两点是否在一条直线的同一边的方式,也就是线段A的两个点都在直线B的同一边,同时线段B的两个点都在直线B的同一边。
- 构建线段A的直线
- 对B的两个顶点,判断是否在直线A的同一侧,如果不是,退出
- 同理对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的值相对会很小,从而整体截断误差小,从而保证更高的准确率。