簡體   English   中英

快速而優雅的已知整數值的單向映射

[英]Fast and elegant one-way mapping of known integer values

我必須將一組已知整數映射到另一組已知整數,一對一關系,所有預定義等等。 所以,假設我有這樣的東西(c ++,簡化了,但是你會明白的):

struct s { int a; int b; };

s theMap[] = { {2, 5}, {79, 12958 } };

現在給定一個輸入整數,例如79,我需要從Map中找到相應的結果(顯然是12958)。 有什么不錯而又快速的方法來代替循環運行嗎? 也歡迎其他數據結構建議,但該地圖應易於在源代碼中手工編寫。

兩組中的值都在0到2 ^ 16的范圍內,並且只有大約130對。 我還想要的是一種非常簡單的靜態初始化數據的方法。

使用地圖

#include <map>
#include <iostream>

int main() {
   std::map <int, int> m;
   m[79] = 12958; 
   std::cout << m[79] << std::endl;
}

使用映射是最通用的解決方案,也是最可移植的(C ++標准尚不支持哈希表,但它們是非常常見的擴展)。 它不一定是最快的。 其他人建議的二進制搜索和哈希圖解決方案都可能(但不會)勝過它。 但是,這對於大多數應用程序可能並不重要。

按鍵對數組進行排序,然后執行二進制搜索。

如果需要編譯時映射,則可以使用以下模板:

// template to specialize
template<int T> struct int2int {};    

// macro for simplifying declaration of specializations
#define I2I_DEF(x, v) template<> struct int2int<x> { static const int value = v; };

// definitions
I2I_DEF(2, 5) I2I_DEF(79, 12958) I2I_DEF(55, 100) // etc.

// use
#include <iostream>    
int main()
{
  std::cout << int2int<2>::value << " " << int2int<79>::value << std::endl;

  return 0;
}

如果您的源整數i數量相對較高(因此直接搜索變得效率低下)但仍可管理,則可以相對輕松地為輸入整數構建完美的哈希函數hash(i) (例如,使用Pearson哈希 ),並且然后使用哈希值作為輸出表map的條目

output = map[hash(i)];

當然,如果輸入值的范圍相對較小,則可以使用恆等函數代替hash而只需將整個對象變成直接重映射

output = map[i];

(盡管是這種情況,您甚至都不會問。)

std::map<int, int> theMap;
theMap[2] = 5;
std::map<int, int>::const_iterator iter = theMap.find(2);
if (iter != theMap.end())
   iter->second; // found it

插入整數對,按鍵,對數復雜度檢索值。 如果您的數據集非常大並且需要更快的檢索速度,請使用std :: tr1 :: unordered_map或boost :: unordered_map(以防您的標准庫沒有TR1實現)。

std :: mapstd :: unordered_map可能是最干凈的了。 不幸的是,C ++沒有內置的關聯數組。

std::map<int,int> mymap; // the same with unordered map

// one way of inserting
mymap.insert ( std::make_pair(2,5) );
mymap.insert ( std::make_pair(79,12958) );

// another
mymap[2] = 5;
mymap[79] = 12958;

去檢查

std::map<int,int>::const_iterator iter = mymap.find(2);
if ( iter != mymap.end() )
{
   // found
   int value = iter->second;
}

map O(log n)相比, unordered_map具有O(1)攤銷查找時間的優勢。

作為補充,如果您需要二進制搜索實現,請不要忽略C ++標准庫。 下面的代碼使用equal_range算法對結構類型的數組執行一個操作(為代碼質量有些怪異而道歉)

#include <algorithm>
#include <iostream>
using namespace std;

struct S {
    int k, v;
};

bool operator <( const S & a, const S & b ) {
    return a.k < b.k;
};

// must be sorted in key order
S values[] = {{42,123},{666,27}};

int main() {

    S t;
    cin >> t.k;

    S * valend = &values[0] + sizeof(values) / sizeof(S);
    pair <S*,S*> pos = equal_range( &values[0], valend , t);

    if ( pos.first != pos.second ) {
        cout << pos.first->v << endl;
    }
    else {
        cout << "no" << endl;
    }
}

為什么不使用哈希圖? 它將為您提供或多或少的恆定檢索時間。

您有正確的想法,這是一張地圖。 使用std :: map

跳轉表。 如果您能夠使用交換機,則可能會進行設置,否則可能需要進行一些組裝,但這可能是最快的方法。

您可以使用boost :: assign。

#include <iostream>
#include <boost/assign.hpp>

int main()
{
    typedef std::map< int, int > int2int_t;
    typedef int2int_t::const_iterator int2int_cit;

    const int2int_t theMap
        = boost::assign::map_list_of
            ( 2, 5 )
            ( 79, 12958 )
            ;

    int2int_cit it = theMap.find( 2 );
    if ( it != theMap.end() )
    {
        const int result = it->second;
        std::cout << result << std::endl;
    }
}

如果您100%確信theMap不會增長到超過1,000個條目(配置文件!),那么執行二進制搜索可能會更快。

如果a的值有一個合理的界線(例如,低於1,000),則可以制作一個簡單的數組,並以a作為保證O(1)復雜度的索引。 如果您使用的是gcc,則可以使用以下語法( http://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html#Designated-Inits ):

int theMap[256] = { [2] = 5, [79] = 12958 };

(很遺憾,g ++不支持此功能)

在其他任何情況下,請使用std::unordered_map ,如其他答案所示。

您的偽代碼幾乎是有效的C ++ 0x代碼-但是C ++ 0x需要的更少!

map<int, int> theMap = { {2, 5}, {79, 12958 } };
assert ( theMap[ 2 ] == 5 );

在“普通” C ++中,您必須像這樣初始化地圖,但仍然很優雅:

pair< int, int > map_array[2] = { make_pair(2, 5), make_pair(79, 12958) };
map< int, int > theMap( &map_array[0], &map_array[2] ); // sorts the array
assert ( theMap[ 2 ] == 5 );

這寫起來很快,運行起​​來也快!

編輯:只是不要使地圖成為全局變量。 (盡管這在C ++ 0x中是安全的。)如果這樣做,則只有在編譯器選擇在map_array之后選擇對其進行初始化時,它才會被正確初始化,這是絕對不能保證的。 如果要成為全局對象,請使用theMap.assign( &map_array[0], &map_array[2] );對其進行初始化theMap.assign( &map_array[0], &map_array[2] );

還有一種稱為“ xmacros”的技術,它也是一種很好的方法,可以准確地完成您正在談論的內容。 但是,很容易濫用該技術,因此我總是建議謹慎使用它。 檢出: http : //en.wikipedia.org/wiki/C_preprocessor#X-Macros

基本要點是,您有一個文件,其中列出了映射文件foo.txt,如下所示:MAP(2,5)
地圖(79,12958)
...

然后,定義一個宏MAP(A,B),它接受這兩個參數並為您進行初始化。 然后#include文件(foo.txt)。 如果您願意,甚至可以通過在文件的每個#include之間重新定義宏來進行多次遍歷。 然后,要添加更多映射,只需將它們添加到foo.txt並重新編譯。 它非常強大,可以用於許多不同的事物。

如果出於某種原因不想使用映射(例如,您只想使用在編譯時設置的數組),則還可以將函子<algorithm>結合使用:

#include <windows.h>
#include <cstdlib>
#include <functional>
#include <algorithm>
#include <iostream>
using namespace std;

struct s { int a; int b; };

s theMap[] = { {2, 5}, {79, 12958 } };

struct match_key : public unary_function<s, bool>
{
    match_key(int key) : key_(key) {};
    bool operator()(const s& rhs) const
    {
        return rhs.a == key_;
    }
private:
    int key_;
};

int main()
{
    size_t mapSize = sizeof(theMap)/sizeof(theMap[0]);
    s* it = find_if(&theMap[0], &theMap[mapSize], match_key(79));
    cout << it->b;

    return 0;
}

暫無
暫無

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

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