[英]How do i sort mathematical vectors by strict weak ordering for a map?
我嘗試編寫一個std :: map <Vector3D,double>,其中colinear(並行或反並行)向量應該共享相同的密鑰。
作為比較函數,我使用以下函數(在isEqualEnough()中具有1e-9容差),這是我在std :: map中使用(數學)向量創建的
struct Vector3DComparator
{
bool operator() (const Vector3D& lhsIn, const Vector3D& rhsIn) const
{
Vector3D lhs = lhsIn.absolute(); // make all members positive
Vector3D rhs = rhsIn.absolute();
if ((lhs.z < rhs.z))
return true;
if ((isEqualEnough(lhs.z, rhs.z))
&& (lhs.y < rhs.y))
return true;
if ((isEqualEnough(lhs.z, rhs.z))
&& (isEqualEnough(lhs.y, rhs.y))
&& (lhs.x < rhs.x))
return true;
return false;
}
};
當我將一個立方體的法線插入我的地圖時,我應該得到3個不同的值(因為我不關心方向)但我得到4:
比較函數在某種程度上是錯誤的,但每當我嘗試修復它時,我得到一個斷言告訴我“表達式:無效的比較器”。
有人發現錯誤嗎?
在數學上不可能對關系運算符使用容差並產生嚴格的弱排序。 任何類型的收斂標准都不能滿足排序算法和數據結構要求。 原因很簡單:使用容差的兩個值的不兼容性不會產生等價關系,因為它不是傳遞的 。 你可能有almostEqual(a, b)
和almostEqual(b, c)
而且~almostEqual(a, c)
。 嘗試使用a=1.0; b=2.0; c=3.0; tolerance=1.5;
a=1.0; b=2.0; c=3.0; tolerance=1.5;
。 您可以看一下這個答案: 浮點數=是否正常? 。
您仍然可以使用截斷,地板,屋頂或圓形函數在浮點數上定義等價關系。 讓我們定義例如less3(a, b)
當且僅當floor(a * 8) < floor(b * 8)
假設a和b是二進制浮點數並且不是NAN並且乘法不產生相同的有符號無窮大; 這比較a和b使用3位精度(十進制0.125)。 現在定義equiv3(a, b)
當且僅當!less3(a, b) && ~less3(b, a)
。 可以證明, eqiv3(a, b)
產生適當的等價關系。 由於less3
是階次關系而equiv3
是等價關系,因此less3
是浮點數(不包括NAN)的嚴格弱階。 此外,在a * 8 == +INF && b * 8 == +INF || a * 8 == -INF && b * 8 == -INF
a * 8 == +INF && b * 8 == +INF || a * 8 == -INF && b * 8 == -INF
你可以使用普通的<operator on floats回退。
將Julien Villemure-Fréchette的答案與@alterigel發布的鏈接結合起來,我的比較函數工作:
struct Vector3DComparator
{
bool operator() (const Vector3D& lhsIn, const Vector3D& rhsIn) const
{
int p = 100000; // precision factor
Vector3D lhs = lhsIn.absolute(); // make all members positive
Vector3D rhs = rhsIn.absolute();
auto lhsTied = std::tie((int)(lhs.x * p), (int)(lhs.y * p), (int)(lhs.z * p));
auto rhsTied = std::tie((int)(rhs.x * p), (int)(rhs.y * p), (int)(rhs.z * p));
return lhsTied < rhsTied;
}
};
請注意:此代碼包含錯誤的樣式,如c風格的演員表,錯誤的命名等等。 我的函數和類與此處發布的函數和類不同。 我只是將所有東西都剝離了以使其易於理解。
編輯:
我注意到還有兩個錯誤:
第一:它並不總是適用於幾乎相同的載體。 根據@tetorea對我的問題的最后評論我改變了函數,總是得到非常相似的值。 我使用點積,因為它對於平行向量是±1(或至少接近)。
第二:.absolute()不起作用,因為有了這個函數,兩個向量(-1,1,0)和(1,1,0)被認為是平行的,它們顯然不是。
在下面的代碼中,您可以找到更正的版本:
struct Vector3DComparator
{
bool operator() (const Vector3D& lhs, const Vector3D& rhs) const
{
if (isEqualEnough(fabs(lhs * rhs), 1.0)) // dot product
return false;
int p = 100000; // precision factor
auto lhsTied = std::tie((int)(lhs.x * p), (int)(lhs.y * p), (int)(lhs.z * p));
auto rhsTied = std::tie((int)(rhs.x * p), (int)(rhs.y * p), (int)(rhs.z * p));
return lhsTied < rhsTied;
}
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.