繁体   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