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的值相對會很小,從而整體截斷誤差小,從而保證更高的準確率。