UPD: Tricks to make unordered_map
faster added.
Hi!
What is unordered_map
?
It is a data structure like map
but it is more than 4 times faster than map
.you can use it in C++11 with including #include<unordered_map>
.for example:
#include<unordered_map>
using namespace std;
int main(){
unordered_map<int,int>mp;
mp[5]=12;
mp[4]=14;
cout<<mp[5]<<' '<<mp[4]<<endl;//prints: 12 14
}
Lets explain it more.
How it works?
Focus on unordered_set
for simplify.You can imagine that it has vector of vector like vector<vector<type> > mp
. Every time you insert value V
in that, it calculate its hash
(I will explain how it works), let hash(V)=K
; it inserts V
into mp[K]
(formally it calls mp[K].push_back(V)
).When you call mp.count(V)
it searchs for V
in mp[K]
.
map
VS unordered_map
(and set
VS unordered_set
)
1-unordered_map
is more than 4 times faster
Focus on problem 527E - Страсти в дата-центре, it seems that it is good to use unordered_map
instead of map
.
My submission with map
: 14269000 Time:484 MS.
My submission with unordered_map
: 14269381 Time:358 MS.
Another good sample to show how fast is unordered_map
is problem 178C3 - Бобер и разрешение коллизий:
My submission with map
: 15781810 Time limit exceeded on test 36 .
My submission with unordered_map
: 15774685 Accepted (Time:966 MS).
2-unordered_set
(and unordered_map
) is not sorted
Please note that set
(and map
) is sorted, for example *(s.begin()) is always smallest number in the set; but in unordered_set
it isn't. similarly there isn't lower_bound
and upper_bound
in unordered_set
(and unordered_map
similarly).
Creating hash function for structs
Now, one other problem remains, you can try to compile this code:
unordered_map<pair<int,int>,int>mp;
You will get Compilation Error! Why? See this page for unordered_map
supported types. For unsupported types, you have to create your own hash function for use. For example lets see how we can create a hash function for pair<int,int>
.
As you know any int
value is between -2^31+1 to 2^31-1.so if we create function that for every pair<int,int>
returns distinct value in type size_t
(alias of unsigned int
), it will be done. It is pretty easy: x.first^(x.second<<32)
is good. but be careful about overflow ;for having good hash function we use hash<long long>
.The code is looks like this:
struct HASH{
size_t operator()(const pair<int,int>&x)const{
return hash<long long>()(((long long)x.first)^(((long long)x.second)<<32));
}
};
unordered_map<pair<int,int>,int,HASH>mp;
Now you have a unordered_map
of pair<int,int>
(it isn't problem what is second member, in example it was int
).Creating hash function for other structs is same.
How to test unordered_map
is faster than map
?
You can test that for N=10^6, unordered_set
(unordered_map
) is more than 4 times faster than set
(map
) ;with this code:(compile code with command: g++ file.cpp -std=c++11 -O2)
#include <bits/stdc++.h>
using namespace std;
unordered_set<int>s;//replace it with set for test.
int main(){
auto T=clock();
s.reserve(32768); //updated !
s.max_load_factor(0.25); //updated !
for(int i=0;i<1000000;i++)
s.insert(i);
cout<<double(clock()-T)/CLOCKS_PER_SEC<<'\n';
}
Note1: Let your hash function H(V)
, it is better that H(V) returns distinct value for every distinct V
, it makes unordered_map
faster; but if you can't do that it doesn't problem. The only problem is that unordered_map
becomes slower(however it is better than map
).
Note2: Please be careful about your hash function time complexly! it is fun to use this hash function:
struct HASH{
size_t operator()(const pair<int,int>&x)const{
size_t ans=0;
for(int i=0;i<x.first;i++)
ans+=x.second;
return ans;
}
};
UPD I will explain hash<type>
in my next post.
UPD It seems that sometimes unordered_map
becames so slow.but it can improve with this two lines of code:
unordered_map<int,int>mp;
mp.reserve(1024);
mp.max_load_factor(0.25);
With this two lines unordered_map
become about 10 times faster. You can replace 1024
with another suitable power of two.(it depends on number of insert
s you will do).
Thank you so much! I had no idea about this. From now I'll use it as much as possible :)
you can use
unordered_set/map
when you don't need order of members.for example one of
unordered_set
applications is when you are storing all of the settings of one vector (for example); you can store them in unordered_set.for example:you will never check the same vectors with that trick!
Unordered_map isn't faster than map when count of elements isn't much. Sorry for bad eng :)
if N is about 100 ;
map
has equal time withunordred_map
.but you can test it when N is about 10^6. it seems that
unordered_map
is about 4 times faster thanmap
.Hi, I need a small clarification. I was just solving this problem. I made submission using both unordered_map and map. But when I include the reserve and max_load_factor, my code gets AC I don't understand why the normal unordered_map gives TLE when N is about 1e5 when it's supposed to be faster than map. Could you please help me with this? Is it because of "sometimes unordered_map becomes so slow" ?
Hey AishwaryaR
When the capacity is not reserved beforehand, it takes capacity as 16 and creates a hashtable on this.
Everytime the number of elements increase than threshold, it doubles this capacity and rehashes all the existing keys.
Reserving an approximate capacity before hand avoids this rehashing and dynamic space allocation, in turn making the program more efficient and reducing the runtime.
Most competitive programming environments are still 32-bit. So, by doing
^(((long long)x.second)<<32)
and then implicitly casting tosize_t
, you are effectively discardingx.second
. Now, the hash depends only onx.first
.Here is the code to check that:
Note
make_pair (1, i)
. The above test takes more than a second locally when compiled in 32-bit mode. You can run a custom test in any Codeforces past contest and check for yourself.Post edited.
Sorry, I don't get what you are saying.
So what? That's still on the order of a second.
Note that we insert 20,000 elements, not a million like you do. If inserting twenty thousand, again, elements takes on the order of a second, that's quadratic behavior, or at least definitely not linear. The constant 20,000 is used instead of your code's 1,000,000 so that we can actually see the result in reasonable time. One can make it 1,000,000 and watch it time out (I, for one, wasn't patient enough to let it finish).
Also, note that changing
make_pair (1, i)
tomake_pair (i, i)
makes the program run in an instant (less than 100 ms).To summarize again:
Codeforces checks your solutions in a 32-bit environment.
So,
size_t
is 32 bits long.So, for every possible
X
andY
, the result ofX ^ (((long long)Y)<<32)
converted tosize_t
is justX
.So, the hashes for pairs
(1, 0)
,(1, 1)
,(1, 2)
,(1, 3)
, ...,(1, 999999)
will be equal, andunordered_map
will have trouble storing them.I have interested! thank you.
I have changed my hash function, you can see the new one.
I have mistaken,
size_t
is alias ofunsigned int
, nounsigned long long int
.The new hash approach perhaps does something like
x * 37 + y
anyway, so why not do that explicitly?On a side note, I'm surprised that a language as mature as C++ (11) does not define a default hashing method for library containers such as pair or vector.
It has one for vector:
hash<vector<int> >
.You can use it for pair, first create a vector of
{x.first,x.second}
and usehash<vector<int> >
.Can you please elaborate?
I try the following:
And it does not compile (MinGW GCC 4.8.1 and 5.1.0), throwing some 100+ lines of error messages at me, basically stating that I misuse
hash<vector <int> >
. Removing()
afterhash<vector<int> >
does not help.Googling for a few minutes did not help me either. I only found a reference stating that it is not implemented in the library, and a few implementations for custom types.
You can create it like this:
So, what you are saying is that there is in fact no built-in
hash<vector<int> >
after all? Then my point still stands.I won't deny that almost anything is possible with C++. I'm just surprised some trivial pieces of that work aren't in the library already.
Hey Gassa! Would you recommend same struct HASH when the order of elements in my pair doesn't matter? for example, in my case pair (1, 2) can be considered same as (2, 1). Is there any faster way for that?
When you store the pairs, always make it such that
pair.first <= pair.second
holds.In fact, my point above was that I don't like the very idea of C++ not having a standard hash of pair in the library, and the 95%+ horrendous implementations of pair hashing that ensue. Plentiful examples around or on StackOverflow.
In particular, no, I don't recommend hashing by just XORing the elements of the pair, as there are naturally occurring cases when it results in suboptimal behaviour.
As for hashing a pair where the order of elements doesn't matter, I'd do that explicitly at first opportunity: when constructing the pair. So that
(a, b)
becomes(min(a,b), max(a,b))
right away. And then use standard pairs.Thank you. I get it now.
I run these two versions of
map
andunordered_map
on custome test with GNU G++11 5.1.0. I see thatunordered_map
is slower thanmap
Version of
map
:Version of
unordered_map
:Why is that?
On Intel® Core™ i3 CPU M 390 @ 2.67GHz × 4 :
map
: 2469 MSunordered_map
: 485 MSOn CodeForces:
map
: 2469 MSunordered_map
: 1885 MShey @Arpa can u explain me the struct HASH code? thanks in advance.....
You should implement
operator ()
for this struct. It takes an object and returns size_t (alias of unsigned int). You should write it in a way such that minimize the number of conflicts.Add this two lines:
Auto comment: topic has been updated by Arpa (previous revision, new revision, compare).
What does this mean ?
s.max_load_factor(0.25);
And which library it uses ?
The load factor is the ratio between the number of elements in the container (its size) and the number of buckets (bucket_count). (source)
it uses
<unordered_map>
(or<unordered_set>
) for sure.Hi can anyone explain, why map is more than 10 times faster in this case? I could not find any reason for this. any help on this matter would be highy appreciated. I also tried adding the 2 lines of code mentioned above to make unordered_map faster but with no luck. 21740384 and 21740655
Because map has a logarithmic access time on size always, on the other hand, unordered_map in average has a constant access time, but in worst case it's just linear in size.
Thanks for the quick reply! I am aware of the worst case complexity of unordered map. However, usually most of the times unordered map is observed to be faster. Can you share any further insights on deciding between unordered map and map. And also what is making unordered map slower than map in the above case I presented? i,e.. what kinds of data is suitable to be represented by map and unordered_map exactly?
EDIT: I see that the test case on which unordered map gave TLE involves hashing a multiple of some fixed number. Has this got something to do with the TLE(I believe it must be the reason). Also, was the testcase specifically designed so that unordered_map fails on it, How do we make sure not to fall in such traps if so?
Please see the following issue: http://codeforces.me/blog/entry/44705?#comment-292160
Thanks a lot! I'm sorry for posting on the wrong blog. I should have found that post myself. But I'm new here :D
This problem can resolved with
reserve
trick.Like this : 21771219.
It's not a trick if you know how unordered_map works.
I know how it works. Read post again carefully.
I have explained in post how it works.
Hi. I have 3 questions.
Why must the input value for the reserve() method be a power of 2?
Is there formula to determine the input value for the reserve() method? Let's say we use mp.reserve(x) where x is a power of 2. Does that mean that x must be >= N, where N is the number of elements that we are going to input into the unordered_map?
How did you determine the value for the max_load_factor? Is that supposed to be a magic value?
Please advise me.
Thank you for your hard work, but unfortunately I don't find it that much useful with all that painful implementation.
AC using map: 33860000
TLE using unordered_map: 33860168 [ Yes, I used the tricks you mentioned to make unordered map faster. Also I have used the Hash function that you gave in the post for pair<int, int>
So, how can I believe that unorded_map is faster than map ??
It's really confusing when to use unordered_map and when to use map. My submission with map takes 1450 ms: 38906751 Same submission with unordered_map takes 467 ms:38906911 Though both passed time limit (3 s), if the time limit was 1-1.3 s, solution using map would have failed.
You just need to use a better hash function for pair<int,int> to get your solution passed. The hash function proposed here for pair<int,int> clearly isn't a good one. Read the above discussion here. I have changed your hash function and your TLE code gets ac for that problem. Here's the ac code with modified hash function.
But is it always safe to use unordered_map in contests, since in worst case unordered_map can be of O(n)? Should anyone use unordered_map always instead of map ?
SUMFOUR — 4 values whose sum is 0 on SPOJ in an example where unordered_map with reserve() will give AC and others will give TLE
ََُArpa youre the best :)
I have used unordered_map for this problem. Christmas Trees I got a TLE with this Then I changed the unordered_map to just map and got AC with this Can you explain what is happening here. [Sorry for my bad English]
Unordered_map : TLE submission link
map : AC submission link
i've no idea why...
It happens. Try to use the
reserve
method. You can implement Hash Map on your own too.Hi. Could you please explain how you arrive with values such as 1024 or 4096 while using
reserve
? Say, for example, I know that in my program there will be 10^7insert
operations. What value do I use then?Doesn't
reserve(4096)
mean set the appropriate number of buckets to hold at least 4096 elements in the hash table without exceeding the max_load_ratio?Be careful with hash data structures. Even though
std::unordered_map
is often faster thanstd::map
, it still has a worst case complexity ofO(n)
per operation and it is possible to design TLE hacks this way. See https://codeforces.me/blog/entry/62393.What is
unordred_map
?Shouldn't it be "What is
unordered_map
?"Thanks. Fixed.
what is the average time complexity of insertion/search in unordered_map if key is string ? Is it O(L) L=>length of string ?
Yes.
Hi I have tried to change (unordered_)map to many thing like this ones but every time I get TLE on last testcase; I think this idea should be change but if anybody can help me, I ll be happy. link of submission
why my code MLE after using mp.reserve(1024)
simply just follow this blog
Thanks.
using std::map is as good as using that custom hash function on std::unordered_map, based on my observations
there are many cases where unordered_map with custom hash will give you slight edge
Yeah that's why I told 'as good as', that minor edge is negligible.