查看: 5880|回复: 0

在GCC和Visual Studio中使用hash_map

[复制链接]
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
跳转到指定楼层
1#
发表于 2010-4-20 13:31 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Mar

25

2010
  在GCC和Visual Studio中使用hash_map
Posted by watashi in summary, tags: cpp, macro, summary
熟悉STL或熟悉ACM/ICPC的话,其中的set, map, multiset, multimap一定用过无数次了,它们都是用平衡二叉树(红黑树)实现的,复杂度为O(lgn)。我们也知道set, map可以通过哈希来实现,复杂度只有O(1),可惜直到现在,unsorted_set或hash_map都没能成为C++标准的一部分(C++0x,- -b)。不过无论在GNU GCC中还是Microsoft Visual Studio中都有对hash_set, hash_map, hash_multiset, hash_multimap的支持。

GCC中的hash_map定义在<ext/hash_map>文件,namespace __gnu_cxx中。要定义一个hash_map<int, int>非常简单:

#include <ext/hash_map>  

using namespace __gnu_cxx;  

hash_map<int, int> hm;
在使用map时,如果我们想要改变元素顺序,或以自定义的struct/class作为key的时候,可以设定map第三个模板参数(默认是less<Key>,即operator<)。对于hash_map,我们需要设定其第三个(hash<Key>)和第四个模板参数(equal_to<Key>, operator==)。

typedef long long my_type;  

typedef int any_type;  

struct my_hash {  

    size_t operator()(const my_type& key) const {  

        return (key >> 32) ^ key;  

    }  

};  

struct my_equal_to {  

    bool operator()(const my_type& lhs, const my_type& rhs) const {  

        return lhs == rhs;  

    }  

};  

hash_map<my_type, any_type, my_hash, my_equal_to> my_hash_map;
对与int等基本类型,系统提供有hash<int>等版本的模板特化,所以只需要指定前两个模板参数就足够了。实现了模板特化的有以下类型

[const] char*, crope, wrope, [signed|unsigned] char, [unsigned] short, [unsigned] int, [unsigned] long
如果需要的话,我们也可以为其他类型实现模板特化

view sourceprint?
01 // hash_map<Key, Tp, HashFn=hash<Key>, EqualKey=equal_to<Key>, Alloc=allocator<Tp> >  

02 #include <cstdio>  

03 #include <utility>  

04 #include <hash_map>  

05 using namespace std;  

06 using namespace __gnu_cxx;  

07   

08 namespace __gnu_cxx {  

09     template<>  

10     struct hash<pair<int, int> > {  

11         size_t operator()(const pair<int, int>& key) const {  

12             return key.first * key.second;  

13         }  

14     };  

15 }  

16 hash_map<pair<int, int>, int> hm;

Visual C++的hash_map定义在<hash_map>文件,namespace stdext中,早先在namespace std中。其实现与GCC的不同,模板参数也不一样,比如上面的例子在VC++版本如下

view sourceprint?
01 // hash_map<Key, Type, Traits=hash_compare<Key, less<Key> >, Allocator=allocator<pair<const Key, Type> > >  

02 >  

03 class hash_map  

04 #include <cstdio>  

05 #include <utility>  

06 #include <hash_map>  

07 using namespace std;  

08 using namespace stdext;  

09   

10 template<>  

11 struct hash_compare<pair<int, int> > {  

12     // the mean number of elements per bucket  

13     static const size_t bucket_size = 4;  

14     // the minimum number of buckets  

15     static const size_t min_buckets = 8;  

16     // hash function  

17     size_t operator()(const pair<int, int>& key) const {  

18         return key.first * key.second;  

19     }  

20     // compare function  

21     bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) const {  

22         return lhs < rhs;  

23     }  

24 };  

25 hash_map<pair<int, int>, int> hm;
相比前面的hash,上面的hash_compare显然要复杂不少。

不过二者提供的方法基本一致,也和std::map和其他STL容器相似。所以对于上面定义的hash_map,我们都可以用下面的代码进行测试

view sourceprint?
01 ...  

02 int main() {  

03     int n;  

04     scanf("%d", &n);  

05     for (int i = 0; i < n; ++i) {  

06         hm[make_pair(i, i)] = i;  

07     }  

08     for (hash_map<pair<int, int>, int>::iterator i = hm.begin(); i != hm.end(); ++i) {  

09         printf("%d ", i->second);  

10     }  

11     printf("\n%d / %d\n", hm.size(), hm.bucket_count());  

12     return 0;  

13 }
n取12时,GCC 4.4.1得到的结果是

0 1 2 3 4 5 6 7 8 9 10 11  

12 / 193
而Visual Studio 2010得到的结果是

0 4 8 1 3 5 7 9 11 2 6 10  

12 / 8
由此我们可以看出二者在hash_map实现上的不同。__gnu_cxx::hash_map保持size<=bucket_count,而以193, 389, 769, 1543…这样近似成倍增长的质数作为bucket_count。stdext::hash_map则保持size<=bucket_size*bucket_count,bucket_count起初为min_buckets=8,不足时以8倍增长。

之所以我们要选择hash_map,是为了获得更高的效率。hash_map比map到底快多少呢,我们通过下面的程序来测试一下。通过__GNUC__和_MSC_VER这两个宏,我们可以把两个版本的代码写到一起

view sourceprint?
01 #include <map>  

02 #include <ctime>  

03 #include <cstdio>  

04 using namespace std;  

05   

06 #if defined(_MSC_VER)  

07 # include <hash_map>  

08 using stdext::hash_map;  

09 #elif defined(__GNUC__)  

10 # include <ext/hash_map>  

11 using __gnu_cxx::hash_map;  

12 #else  

13 # error 悲剧啊  

14 #endif  

15   

16 int main() {  

17     clock_t S;  

18   

19     S = clock();  

20     for (int i = 0; i < 20; ++i) {  

21         hash_map<int, int> H;  

22         for (int i = 0; i < (1 << 20); ++i) {  

23             H[i] = i;  

24         }  

25     }  

26     printf("hash_map: %.2lfs\n", (double)(clock() - S) / CLOCKS_PER_SEC);  

27   

28     S = clock();  

29     for (int i = 0; i < 20; ++i) {  

30         map<int, int> M;  

31         for (int i = 0; i < (1 << 20); ++i) {  

32             M[i] = i;  

33         }  

34     }  

35     printf("map: %.2lfs\n", (double)(clock() - S) / CLOCKS_PER_SEC);  

36   

37     return 0;  

38 }
结果得到的是(gcc和VS的结果分别来自不同机器,因此没有可比性)

* g++ -O2  

hash_map: 5.39s  

map: 12.35s  

* VS2010 Release  

hash_map: 9.53s  

map: 9.44s
发现g++里hash_map确实要比map快不少,而Visual Studio 2010就是个悲剧,信hash_map不如信春哥啊。

嘛,hash_map确实可能带来一些performance,但不那么stable,所以我们可以考虑优先使用hash_map,而将map最为fallback备胎。

view sourceprint?1 #define USE_HASH_MAP ?  

2 #if USE_HASH_MAP  

3 #include <ext/hash_map>  

4 typedef __gnu_cxx::hash_map<int, int> Hash;  

5 #else  

6 #include <map>  

7 typedef std::map<int, int> Hash;  

8 #endif

不过在浙大校赛这种judge是优秀的gcc,而比赛环境是混乱的VC6的比赛里。hash_map什么的还是能不用就不用吧……

This entry was posted on Thursday, March 25th, 2010 at 9:34 pm and is filed under summary. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

[[i] 本帖最后由 〇〇 于 2010-4-20 13:35 编辑 [/i]]

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表