don't need dictionary's keys - hash table?

K

kdotsky

Hello,
I am using some very large dictionaries with keys that are long strings
(urls). For a large dictionary these keys start to take up a
significant amount of memory. I do not need access to these keys -- I
only need to be able to retrieve the value associated with a certain
key, so I do not want to have the keys stored in memory. Could I just
hash() the url strings first and use the resulting integer as the key?
I think what I'm after here is more like a tradition hash table. If I
do it this way am I going to get the memory savings I am after? Will
the hash function always generate unique keys? Also, would the same
technique work for a set?

Any other thoughts or considerations are appreciated.

Thank You.
 
K

kdotsky

Hello,
I am using some very large dictionaries with keys that are long strings
(urls). For a large dictionary these keys start to take up a
significant amount of memory. I do not need access to these keys -- I
only need to be able to retrieve the value associated with a certain
key, so I do not want to have the keys stored in memory. Could I just
hash() the url strings first and use the resulting integer as the key?
I think what I'm after here is more like a tradition hash table. If I
do it this way am I going to get the memory savings I am after? Will
the hash function always generate unique keys? Also, would the same
technique work for a set?

I just realized that of course the hash is not always going to be
unique, so this wouldn't really work. And it seems a hash table would
still need to store the keys (as strings) so that string comparisons
can be done when a collision occurs. I guess there's no avoiding
storing they keys?
 
F

Fredrik Lundh

I just realized that of course the hash is not always going to be
unique, so this wouldn't really work. And it seems a hash table would
still need to store the keys (as strings) so that string comparisons
can be done when a collision occurs.

btw, Python's dictionary type *is* a highly-optimized implementation of
a "traditional hash table".

</F>
 
D

Diez B. Roggisch

Hello,
I am using some very large dictionaries with keys that are long strings
(urls). For a large dictionary these keys start to take up a
significant amount of memory. I do not need access to these keys -- I
only need to be able to retrieve the value associated with a certain
key, so I do not want to have the keys stored in memory. Could I just
hash() the url strings first and use the resulting integer as the key?
I think what I'm after here is more like a tradition hash table.

python dictionaries are "traditional" hash-tables.
If I
do it this way am I going to get the memory savings I am after? Will
the hash function always generate unique keys? Also, would the same
technique work for a set?

Any other thoughts or considerations are appreciated.

You could try and create a md5 sum of your strings and use that as key. It
_should_ be good enough, but I'm no crypto expert so take that with a grain
of salt.

Diez
 
N

Nick Vatamaniuc

Dictionaries are hash tables in Python.

If you don't really want to store the keys, just use
dic[hash(key)]=value, this way the dictionary will have the same shape
and distribution of keys as dic[key]=value because
hash('abc')=hash(hash('abc')) but the long string of actual keys are
not referenced by the dictionary.

Now you couldn't do dic.keys() and see your urls, and every time you
want to do dic['abc'] you would get a KeyError exception.

Hope this helps,
Nick Vatamaniuc
 
D

Dennis Lee Bieber

On 12 Jul 2006 08:56:56 -0700, (e-mail address removed) declaimed the following
in comp.lang.python:

This is the second thread this week in which someone inquires about
using hash() on lots of URLs... if a third one appears I'd suspect a
homework assignment...
key, so I do not want to have the keys stored in memory. Could I just
hash() the url strings first and use the resulting integer as the key?

Hash functions are not guaranteed to produce UNIQUE values. They
typically are used to determine an entry point in a presized array.
Since multiple data values may have the same hash value, one needs to
account for collisions. One determines collisions by comparing the data
value against the value found in the table (not the hash value -- you
still need access to the original data value) and "jumping" (based upon
some algorithm) if the values do not match... Or the initial table
contains pointers to (linked) lists of the real values.
I think what I'm after here is more like a tradition hash table. If I
do it this way am I going to get the memory savings I am after? Will
the hash function always generate unique keys? Also, would the same
technique work for a set?
No, No, Sets already use the Python method (typically the object ID)
to determine uniqueness, though for strings it may be straight
comparisons -- I've not used Python sets (heck, I only just upgraded to
2.4 a few weeks ago).

For "tradition" hash table:
http://www.sparknotes.com/cs/searching/hashtables/section1.html
http://en.wikipedia.org/wiki/Hashtable

Hash tables are not a data storage optimization -- they ARE a data
/search/ optimization...

If storage space is a concern, and not look-up time, I'd suggest a
tree structure, based upon the parts of the URL... Using the above
links, a tree of the form:

{ "http" : [
{ "www.sparknotes.com" : [
{ "cs" : [
{ "searching" : [
{ "hashtables" : [
"section1.html" ]
} ]
} ]
} ,
{ "en.wikipedia.ord" : [
{ "wiki: [
"Hashtable" ]
} ]
} ] ,
"ftp" : [ ... ]
}

This structure stores each component of the URL only ONCE, so you
don't have all the repeated data for nodes within a site. Since you know
they are URLs, the "://" is implicit for the top level look-up, and a
"/" is implicit for each node. The end of the tree is reached when the
item found in that level's list is not a dictionary. NOTE: you may have
to handle the case of an "implicit" page using dictionary with a null
entry as a special flag:

http://some.site/ #default index.html
http://some.site/aPage.htm #explicit page

{ "http" : [
{ "some.site" : [ "",
"aPage.htm" ]
} ]
}

This is only reasonable for in-memory data where the prime concern
is memory size and not processing to insert/retrieve/delete data. If an
RDBM is going to be used, it would be better to just rely upon the DBMS
to be decently optimized for indexing operations using the full URL.
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
F

Fredrik Lundh

Nick said:
If you don't really want to store the keys, just use
dic[hash(key)]=value, this way the dictionary will have the same shape
and distribution of keys as dic[key]=value because
hash('abc')=hash(hash('abc')) but the long string of actual keys are
not referenced by the dictionary.

how come you're so sure that there will never be any collisions ?

</F>
 
T

Tim Chase

how come you're so sure that there will never be any collisions ?

because none of his strings want their insurance to go up...

:*)

-tkc
 
N

Nick Vatamaniuc

Fred,

I never said there will be no collisions. For clarity, can you quote
the exact phrase where I mentioned that?

To say that there will be no collision is nonsense because the # of
possible long url strings is certainly larger than 2^32, applying the
pigeon hole principle you could easily show that there will be
collisions.

The point is to make collision as unlikely as possible for the human
readable text (that is make them as uniform as possible), that is why
creating good cryptographic hashes is not easy. Not that the Python
hash function work as hard as an MD5 (it is probably a multiplication
and a modulo if I had to guess). But this is a topic beyond scope of
this thread.

The original post just said that he wanted to index some values by
their urls and didn't really care about the urls themselves, so I
suggested that he just use the hash of the key as the key. The
dictionary will then take a hash of that [note that:
hash(string)=hash(hash(string)) ] then the dictionary will not keep
the reference to the urls and GC will collect them. So instead of
dic[urlstring']=value he will do dic[hash(urlstring)]=value. But then
to retrieve he will have to do old_value=dic[hash(urlstring]].

Hopefully this make my point more clear,
Nick V.

Fredrik said:
Nick said:
If you don't really want to store the keys, just use
dic[hash(key)]=value, this way the dictionary will have the same shape
and distribution of keys as dic[key]=value because
hash('abc')=hash(hash('abc')) but the long string of actual keys are
not referenced by the dictionary.

how come you're so sure that there will never be any collisions ?

</F>
 
F

Fredrik Lundh

Nick said:
I never said there will be no collisions. For clarity, can you quote
the exact phrase where I mentioned that?

the text I quoted is only true if you can guarantee that there will be
no collisions.

</F>
 
D

Diez B. Roggisch

Please don't top post. I had to fix that below.
> It should be enough but it might be a little slower than hash(string).

hash alone won't be sufficient, as it is guaranteed to have to much
collisions. The OP already figured that out, btw.

Diez
 
P

Paul Rubin

do it this way am I going to get the memory savings I am after? Will
the hash function always generate unique keys? Also, would the same
technique work for a set?

A hash function that always generates unique hashes is called a
perfect hash. They tend to be slow, and of course, to generate one,
you need to know all the keys in advance.

If you use a long cryptographic hash like md5 or sha, the chances of
collision is very small. That's probably your best bet.

In general, if your hash is N bits long, you will probably get
collisions once you have around 2**(N/2) keys. sha is a 160
bit hash so getting collisions by accident is extremely unlikely.
 
T

Terry Hancock

Nick said:
The original post just said that he wanted to index some values by
their urls and didn't really care about the urls themselves, so I
suggested that he just use the hash of the key as the key. The
dictionary will then take a hash of that [note that:
hash(string)=hash(hash(string)) ] then the dictionary will not keep
the reference to the urls and GC will collect them. So instead of
dic[urlstring']=value he will do dic[hash(urlstring)]=value. But then
to retrieve he will have to do old_value=dic[hash(urlstring]].

Note that it is trivial to catch collisions on entry and correct them:

key = hash(url_string)
i = 0
if d.has_key(key):
while d.has_key(key):
i += 1
d[key + repr(i)] = val
else:
d[key] = val

or something along those lines. No need to keep the original string.

Obviously this is only efficient if collisions are rare.

I am a little surprised that hash(hash(s)) == hash(s), is that actually
true?

Cheers,
Terry
 
G

Ganesan Rajagopal

Terry" == Terry Hancock said:
Note that it is trivial to catch collisions on entry and correct them:
key = hash(url_string)
i = 0
if d.has_key(key):
while d.has_key(key):
i += 1

hash is a number. It's sufficient to do

while d.has_key(key):
key += 1
I am a little surprised that hash(hash(s)) == hash(s), is that actually
true?
42

Ganesan
 
P

Paul Rubin

Ganesan Rajagopal said:
hash is a number. It's sufficient to do

while d.has_key(key):
key += 1

This is called linear probing and is not considered that great a
collision resolution strategy for hash tables. Really, if you want an
exhaustive study about hashing, see Knuth vol 3. If you're just
trying to get the application doing something reasonable, let the
database do its job.
 
N

Nick Vatamaniuc

Yeah hash(hash(immutable))=hash(immutable) it seems. Not sure this is a
specification but it happens that way:
------------------------------------------
$ >>> hash('abc')
-1600925533
$ >>> hash(hash('abc'))
-1600925533
$ >>> hash(hash(hash(('abc'))))
-1600925533-----------------------------------------
Of course then hash(0)=0, hash(1)=0 and also hash(2**32)=1, all this in
standard CPython on IA32.

Nick Vatamaniuc
 
D

Diez B. Roggisch

I am a little surprised that hash(hash(s)) == hash(s), is that actually

As hashes are integers & the hash of an integer is the integer itself,
this isn't to surprising.


Diez
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top