簡體   English   中英

std :: map中的容錯鍵查找

[英]Tolerant key lookup in std::map

要求:

  1. 容器根據數字比較鍵進行排序(例如std :: map)
  2. 根據浮動容差檢查密鑰是否存在(例如map.find()並使用自定義比較器)
  3. 而且棘手的是:比較器使用的浮動公差可以由用戶在運行時更改!

前兩個可以使用帶有自定義比較器的地圖完成:

struct floatCompare : public std::binary_function<float,float,bool>
{
    bool operator()( const float &left, const float &right ) const
    {
        return (fabs(left - right) > 1e-3) && (left < right);
    }
};

typedef std::map< float, float, floatCompare > floatMap;

使用此實現,floatMap.find(15.0001)將在地圖中找到15.0。

但是,假設用戶不希望浮動容差為1e-3。 使比較器函數在運行時使用可變容差的最簡單方法是什么? 我不介意每次更新epsilon時都會根據新的比較器重新創建和重新排序地圖。

這里初始化之后修改的其他帖子以及使用浮動作為鍵在這里沒有提供完整的解決方案。

您無法在創建map后更改map的順序(並且您應該只使用普通的舊operator<偶數為此處的浮點類型),並且您甚至無法使用“容錯”比較運算符,因為這可能違反了要求嚴格弱map以保持map狀態。

但是,您可以使用lower_boundupper_bound進行容錯搜索。 要點是你要創建一個類似於equal_range的包裝函數,為“值 - 容差”執行lower_bound ,然后為“value + tolerance”執行upper_bound ,並查看它是否創建了與條件匹配的非空范圍值。

一旦實例化后,您無法更改元素在map中的排序方式。 如果您要找到一些技術黑客(例如實現一個可以在運行時更改的容差的自定義比較器),它將喚起未定義的行為。

更改排序的主要替代方法是使用不同的排序方案創建另一個映射。 這個其他映射可以是索引映射,其中鍵以不同的方式排序,並且值不是元素本身,而是主映射中的索引。

或者也許你真正想做的不是改變順序,而是保持順序並改變搜索參數。

你可以做,有幾種方法可以做到。

一種是簡單地使用map::lower_bound - 一次使用公差的下限,一次使用公差的上限,剛好超過公差的結束。 例如,如果要查找容差為1e-5的15.0 您可以使用14.99995進行lower_bound ,然后使用15.00005(我的數學可能不在此處)再次找到該范圍內的元素。

另一種方法是將std::find_if與自定義函子,lambda或std::function 您可以以構造中的公差和值來聲明仿函數,並執行check in operator()

由於這是一個家庭作業問題,我將留下實際執行所有這些的繁瑣細節。 :)

使用簡單的自定義比較器無法實現這一點,即使可以在定義后更改它,也不能使用新的比較器。 事實是:“寬容比較器”並不是真正的比較器。 對於三個值,有可能a < c (差異足夠大)但是a < bb < c (兩者之間的差異都太小)。 示例: a = 5.0, b = 5.5, c = 6.0, tolerance = 0.6

你應該做的是使用operator< for floats使用默認排序,即只是不提供任何自定義比較器。 然后,對於查找,不要使用find ,而是使用具有根據容差修改的值的lower_boundupper_bound 這兩個函數調用將為您提供兩個迭代器,這兩個迭代器定義將使用此容差接受的序列。 如果此序列為空,則顯然未找到密鑰。

然后,您可能希望獲得最接近要搜索的值的鍵。 如果這是真的,那么你應該找到這個子序列的min_element ,使用比較器來考慮密鑰和要搜索的值之間的差異

template<typename Map, typename K>
auto tolerant_find(const Map & map, const K & lookup, const K & tolerance) -> decltype(map.begin()) {
    // First, find sub-sequence of keys "near" the lookup value
    auto first = map.lower_bound(lookup - tolerance);
    auto last = map.upper_bound(lookup + tolerance);

    // If they are equal, the sequence is empty, and thus no entry was found.
    // Return the end iterator to be consistent with std::find.
    if (first == last) {
        return map.end();
    }

    // Then, find the one with the minimum distance to the actual lookup value
    typedef typename Map::mapped_type T;
    return std::min_element(first, last, [lookup](std::pair<K,T> a, std::pair<K,T> b) {
        return std::abs(a.first - lookup) < std::abs(b.first - lookup);
    });
}

演示: http//ideone.com/qT3JIa

而不是使用具有容差的比較器,它將以微妙的方式失敗,只需使用從浮點值派生的一致鍵。 使用舍入使浮點值保持一致。

inline double key(double d)
{
    return floor(d * 1000.0 + 0.5);
}

最好只保留std::map類(好吧,部分至少),然后編寫自己的類來實現你提到的三種方法。

template<typename T>
class myMap{
 private:
   float tolerance;
   std::map<float,T> storage;

 public:
   void setTolerance(float t){tolerance=t;};
   std::map<float,T>::iterator find(float val); // ex. same as you provided, just change 1e-3 for tolerance
   /* other methods go here */
};

話雖這么說,我認為您不需要重新創建容器並根據容差對其進行排序。

根據浮動公差檢查密鑰是否存在

僅僅意味着您必須檢查元素是否存在。 地圖內元素的位置不應改變。 你可以從val-tolerance開始搜索,當你找到一個元素(函數find返回一個iterator )時,獲取下一個元素,直到你到達map的末尾或者直到它們的值超過val+tolerance

這基本上意味着insert / add / [] /任何函數的行為不是基於tolerance ,因此存儲值沒有真正的問題。

如果你害怕元素太接近彼此,你可能想要從val開始搜索,然后逐漸增加toleration直到達到用戶所需的容差。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM