7

Looking at the book Mining of Massive Datasets, section 1.3.2 has an overview of Hash Functions. Without a computer science background, this is quite new to me; Ruby was my first language, where a hash seems to be equivalent to Dictionary<object, object>. And I had never considered how this kind of datastructure is put together.

The book mentions hash functions, as a means of implementing these dictionary data structures. This paragraph:

First, a hash function h takes a hash-key value as an argument and produces a bucket number as a result. The bucket number is an integer, normally in the range 0 to B − 1, where B is the number of buckets. Hash-keys can be of any type. There is an intuitive property of hash functions that they “randomize” hash-keys

What exactly are buckets in terms of a hash function? it sounds like buckets are array-like structures, and that the hash function is some kind of algorithm / array-like-structure search that produces the same bucket number every time? What is inside this metaphorical bucket?

I've always read that javascript objects/ruby hashes/ etc don't guarantee order. In practice I've found that keys' order doesn't change (actually, I think using an older version of Mozilla's Rhino interpreter that the JS object order DID change, but I can't be sure...).

Does that mean that hashes (Ruby) / objects (JS) ARE NOT resolved by these hash functions?

Does the word hashing take on different meanings depending on the level at which you are working with computers? i.e. it would seem that a Ruby hash is not the same as a C++ hash...

Zach Smith
  • 8,458
  • 13
  • 59
  • 133

4 Answers4

10

When you hash a value, any useful hash function generally has a smaller range than the domain. This means that out of a large list of input values (for example all possible combinations of letters) it will output any of a smaller list of values (a number capped at a certain length). This means that more than one input value can map to the same output value.

When this is the case, the output values are refered to as buckets.

Consider the function f(x) = x mod 2

This generates the following outputs;

1 => 1
2 => 0
3 => 1
4 => 0

In this case there are two buckets (1 and 0), with a bunch of input values that fall into each.

A good hash function will fill all of these 'buckets' equally, and so enable faster searching etc. If you take the mod of any number, you get the bucket to look into, and thus have to search through less results than if you just searched initially, since each bucket has less results in it than the whole set of inputs. In the ideal situation, the hash is fast to calculate and there is only one result in each bucket, this enables lookups to take only as long as applying the hash function takes.

This is a simplified example of course but hopefully you get the idea?

Milney
  • 6,253
  • 2
  • 19
  • 33
  • Thanks. That's pretty helpful. Is there a tradeoff between using a hash function that guarantees unique bucket assignment vs a hash function that allows for multiple values in the same bucket? In this case it sounds like hash tables are "underwritten" by arrays (so to speak), with buckets being arrays? – Zach Smith May 03 '17 at 14:48
  • 1
    @ZachSmith This will vary by implementation but yes usually more than one input will map to the same output, meaning that they need to be stored in some form of collection if the hash function is used in a data structure, this would indeed usually be an array - but not in every case. The number of inputs that map to each output will vary on the data used, which is why some hash functions can be 'good' for some purposes but rubbish for others – Milney May 03 '17 at 14:50
  • @ZachSmith Yes hashing is a concept so can be used for many purposes, but indeed when used in a data structure usually work in the way you assumed – Milney May 03 '17 at 14:51
4

The concept of a hash function is always the same. It's a function that calculates some number to represent an object. The properties of this number should be:

  • it's relatively cheap to compute
  • it's as different as possible for all objects.

Let's give a really artificial example to show what I mean with this and why/how hashes are usually used.

Take all natural numbers. Now let's assume it's expensive to check if 2 numbers are equal.

Let's also define a relatively cheap hash function as follows:

hash = number % 10

The idea is simple, just take the last digit of the number as the hash. In the explanation you got, this means we put all numbers ending in 1 into an imaginary 1-bucket, all numbers ending in 2 in the 2-bucket etc...

Those buckets don't really exists as data structure. They just make it easy to reason about the hash function.


Now that we have this cheap hash function we can use it to reduce the cost of other things. For example, we want to create a new datastructure to enable cheap searching of numbers. Let's call this datastructure a hashmap.

Here we actually put all the numbers with hash=1 together in a list/set/..., we put the numbers with hash=5 into their own list/set ... etc.

And if we then want to lookup some number, we first calculate it's hash value. Then we check the list/set corresponding to this hash, and then compare only "similar" numbers to find our exact number we want. This means we only had to do a cheap hash calculation and then have to check 1/10th of the numbers with the expensive equality check.

Note here that we use the hash function to define a new datastructure. The hash itself isn't a datastructure.

Imus
  • 802
  • 6
  • 11
1

Consider a phone book.

Imagine that you wanted to look for Donald Duck in a phone book.

It would be very inefficient to have to look every page, and every entry on that page. So rather than doing that, we do the following thing:

  • We create an index

  • We create a way to obtain an index key from a name

For a phone book, the index goes from A-Z, and the function used to get the index key, is just getting first letter from the Surname.

In this case, the hashing function takes Donald Duck and gives you D. Then you take D and go to the index where all the people with Surnames starting with D are.

That would be a very oversimplified way to put it.

arboreal84
  • 2,086
  • 18
  • 21
0

Let me explain in simple terms. Buckets come into picture while handling collisions using chaining technique ( Open hashing or Closed addressing)

Here, each array entry shall correspond to a bucket and each array entry (if nonempty) will be having a pointer to the head of the linked list. (The bucket is implemented as a linked list).

The hash function shall be used by hash table to calculate an index into an array of buckets, from which the desired value can be found.

That is, while checking whether an element is in the hash table, the key is first hashed to find the correct bucket to look into. Then, the corresponding linked list is traversed to locate the desired element.

Similarly while any element addition or deletion, hashing is used to find the appropriate bucket. Then, the bucket is checked for presence/absence of required element, and accordingly it is added/removed from the bucket by traversing corresponding linked list.

Karthik Balaguru
  • 7,424
  • 7
  • 48
  • 65