![](/img/trans.png)
[英]Is there an elegant and fast way to test for the 1-bits in an integer to be in a contiguous region?
[英]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 :: map或std :: 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.