哈希 - 它做什么?

时间:2010-12-11 16:25:49

标签: algorithm

所以我一直在阅读Hashing的期末考试,而我似乎无法理解正在发生的事情。有人能向他解释Hashing他们理解它的最佳方式吗?

对于这个模糊的问题感到抱歉,但我希望你们能够说“哈希是什么”,所以我至少要有一个开始,如果有人知道任何有用的方法来理解它,那也会有所帮助

7 个答案:

答案 0 :(得分:4)

哈希是一种快速启发式方法,用于查找对象的等价类。

用更小的词来说:

散列很有用,因为它在计算上很便宜。成本与等价类的大小无关。 http://en.wikipedia.org/wiki/Time_complexity#Constant_time

等价类是一组等价的项。想想数字的字符串表示。您可能会说“042”,“42”,“42.0”,“84/2”,“41.9 ......”是相同基础抽象概念的等效表示。他们将处于同一等级阶层。 http://en.wikipedia.org/wiki/Equivalence_class

如果我想知道“042”和“84/2”是否等价,我可以计算每个(一个便宜的操作)的哈希码,并且只有当哈希码相等时,我才会尝试更昂贵的检查。如果我想将数字的表示划分为桶,那么相同数字的表示在桶中,我可以通过哈希码选择桶。

哈希是启发式,即它并不总能产生完美的结果,但是它的缺陷可以由知道它们的算法设计者来缓解。散列产生哈希码。两个不同的对象(不在同一个等价类中)可以生成相同的哈希代码,但通常不会,但同一个等价类中的两个对象必须生成相同的哈希代码。 http://en.wikipedia.org/wiki/Heuristic#Computer_science

答案 1 :(得分:3)

Hashing正在总结。

数字序列(2,3,4,5,6)的散列是这些数字的摘要。例如,图20是一种不完整地包括原始数据中的所有可用位的摘要。这不是一个很好的总结,但它是一个总结。

当值涉及多个字节的数据时,某些位必须被拒绝。如果你使用sum和mod(例如,将总和保持在20亿以下),你往往会保留很多最右边的位并丢失所有最左边的位。

所以好的哈希是公平的 - 它公平地保留和拒绝比特。这往往可以防止碰撞。

例如,我们简单化的“和散列”将在其他数字序列之间发生碰撞,这些数字碰巧也会有相同的总和。

答案 2 :(得分:1)

你可以确定性地获取一些数据,单向计算一些固定长度的数据,当你稍微改变输入时,它会完全改变。

答案 3 :(得分:1)

应用于某些数据的哈希函数会生成一些新数据。 对于相同的数据,它始终是相同的。 这就是它。

经常放在它上面的另一个约束,我认为并非如此,散列函数要求你不能从散列中得出原始数据。 对我来说,这是一个自己的类别,称为加密或单向散列。

对某些类型的哈希函数有很多要求

例如,散列的长度始终相同。

或哈希值随机分配给任何给定的输入数据序列。

唯一重要的一点是它的确定性(对于相同的数据总是相同的哈希)。

因此您可以使用它来验证数据完整性,验证密码等。

在这里阅读所有相关内容

http://en.wikipedia.org/wiki/Hash_function

答案 4 :(得分:1)

您应首先阅读wikipedia article。然后就你不理解的主题提出问题。

简而言之,引用文章,哈希意味着:

  

切碎和混合

也就是说,给定一个值,你可以得到另一个(通常)更短的值( chop ),但是即使原始值的一小部分发生变化,所获得的值也应该改变(混合)。

让我们以x % 9为例说明散列函数。

345 % 9 = 3
355 % 9 = 4
344 % 9 = 2
2345 % 9 = 5

您可以看到此散列方法会考虑输入的所有部分,并在任何数字发生变化时进行更改。这使它成为一个很好的散列函数。

另一方面,如果我们采取x%10。我们会得到

345 % 10 = 5
355 % 10 = 5
344 % 10 = 4
2345 % 10 = 5

正如您所看到的,大多数散列值都是5。这告诉我们x%10是一个比x%9更糟糕的散列函数。

请注意,x%10仍然是哈希功能。身份函数也可以被认为是哈希函数。

答案 5 :(得分:1)

首先我们应该说一下Hashing算法要解决的问题。

假设您有一些数据(可能是数组,树或数据库条目)。您希望在此数据存储区中找到具体元素(例如在数组中),但速度要快得多。怎么做?

构建此数据存储区时,可以为每个项目计算特殊值(名为HashValue)。计算此值的方法可能不同。但是所有方法都应该满足特殊条件:每个项目的计算值应该是唯一的。

所以,现在你有一个项目数组,每个项目你有这个HashValue。如何使用它?考虑一下有N个元素的数组。我们根据他们的HashHalues将你的物品放到这个数组中。

假设您要回答这个问题:此数组中是否存在“it1”项?要回答它,您可以简单地找到“it1”的HashValue(让我们称之为f(“it1”))并查看f(“it1”)位置的数组。如果此位置的元素不为null(并且等于我们的“it1”项),我们的答案是正确的。否则回答是错误的。

还存在碰撞问题:如何找到这样最酷的函数,它将为所有不同的元素提供唯一的HashValues。实际上,这种功能不存在。有很多好的功能,可以给你很好的价值。

更好理解的一些例子:

假设您有一个字符串数组:A = {“aaa”,“bgb”,“eccc”,“dddsp”,...}。你要回答这个问题:这个数组是否包含字符串S?

首先,我们要选择计算HashValues的函数。让我们使用函数f,它具有这个意义 - 对于给定的字符串,它返回该字符串的长度(实际上,它是非常糟糕的函数。但是为了便于理解,我把它拿走了。)

所以,f(“aaa”)= 3,f(“qwerty”)= 6,等等......

现在我们要为数组A中的每个元素计算HashValues:f(“aaa”)= 3,f(“eccc”)= 4,...

让我们拿一个数组来保存这些项目(它也命名为HashTable) - 让我们称之为H(一个字符串数组)。所以,现在我们根据他们的HashValues将我们的元素放到这个数组中:

H [3] =“aaa”,H [4] =“eccc”,......

最后,如何在这个数组中找到给定的String?

假设您有一个String s =“eccc”。 f(“eccc”)= 4.所以,如果H [4] ==“eccc”,我们的答案将是真的,否则填写是假的。

但是如何避免情况,何时元素具有相同的HashValues?它有很多方法。其中之一:HashTable中的每个元素都包含一个项目列表。所以,H [4]将包含所有项目,HashValue等于4.以及如何找到具体元素?这很简单:计算此项HashValue并查看HashTable [HashValue]中的项目列表。如果其中一个项目等于我们的搜索元素,则答案为真,其中答案为假。

答案 6 :(得分:1)

我说linut的答案非常好,但我会稍微放大一点。计算机非常擅长访问数组中的东西。如果我知道某个项目在MyArray [19]中,我可以直接访问它。散列函数是将查找键映射到数组下标的一种方法。如果我在一个数组中存储了193,372个不同的字符串,并且我有一个函数将为其中一个字符串返回0,为另一个字符串返回1,为另一个字符串返回2,等等到最后一个为193,371,我可以看看是否有任何给定string在数组中运行该函数,然后查看给定的字符串是否与数组中该点中的字符串匹配。很好,很容易。

不幸的是,在实践中,事情很少是如此美好和整洁。虽然通常可以编写一个函数,将输入映射到一个很好的简单范围内的唯一整数(如果没有别的话:

  if (inputstring == thefirststring) return 0;
  if (inputstring == thesecondstring) return 1;
  if (inputstring == thethirdstring) return 1;
... up to the the193371ndstring

在许多情况下,“完美”功能需要花费大量精力才能计算出不值得的努力。

相反,要做的是设计一个系统,其中哈希函数说明应该从哪里开始查找数据,然后使用其他方法从那里搜索数据。一些常见的方法是:

  1. 线性散列 - 如果两个项映射到相同的散列值,则将其中一个存储在散列码指示的数组插槽中。在查找项目时,在指示的插槽中搜索,然后搜索下一个,然后搜索下一个,等等,直到找到该项目或者找到一个空插槽。线性散列很简单,但除非表格大于其中的项目数(留下大量空插槽),否则它的效果很差。还要注意,从这样的哈希表中删除项目可能是困难的,因为项目的存在可能已经阻止某些其他项目进入其指示的点。
  2. 双重哈希 - 如果两个项目映射到相同的值,则为添加的第二个项目计算不同的哈希值,并将第二个项目移开多个时隙(如果该时段已满,请继续步进增加直到找到一个空位)。如果哈希值是独立的,则此方法可以与更密集的表一起使用。但是,从这样的表中删除项目比使用线性哈希表更难,因为没有很好的方法来查找被要删除的项目替换的项目。
  3. 嵌套散列 - 散列表中的每个插槽都包含一个使用与主表不同的函数的散列表。如果两个散列函数是独立的,那么这可以很好地工作,但如果它们不是,则很容易工作。
  4. Chain-bucket哈希 - 哈希表中的每个槽都包含映射到该哈希值的事物列表。如果N个事物映射到特定的时隙,找到其中一个将花费时间O(N)。但是,如果散列函数是正常的,大多数非空插槽只包含一个项目,大多数那些只包含两个,等等,所以没有插槽可以容纳很多项目。

当处理固定数据集(例如编译器的关键字集)时,线性散列通常是好的;如果它工作不正常,可以调整哈希函数,以便它可以很好地工作。处理未知数据集时,链桶散列通常是最好的方法。处理额外列表的开销可能会使它比双重哈希更昂贵,但它的可能性要小得多。