哈希表调整大小:我们如何在不知道密钥的情况下做到这一点?
Posted
技术标签:
【中文标题】哈希表调整大小:我们如何在不知道密钥的情况下做到这一点?【英文标题】:Hash table resizing: how do we do it without knowing the key? 【发布时间】:2012-12-08 23:32:10 【问题描述】:当我们只在哈希表中存储键的结果数据时,如何执行哈希表调整大小?我能想到的一个例子是存储用户名和密码组合。为了隐私起见,我们只存储密码(也可以有许多其他用例)。在这里,表只存储数据而不是键。鉴于此,如果我想在调整大小期间将条目从旧表复制到新表,我没有散列的密钥。
这里是如何调整大小的?
【问题讨论】:
注意,hashtable 和 table of hashes 是两个完全不同的概念。听起来您将两者混为一谈,由此产生的混乱在任何一种情况下都没有多大意义。 “哈希表”??我同意我的写作可能模棱两可,但我不确定我什么时候写的/是什么意思? 如果您将哈希存储在查找表中,而没有任何指向原始数据的链接,那么您将拥有一个 哈希表——无论您是否使用hashtable 与否。如果您有一个哈希表,那么哈希就是数据。听起来你想要的是一个哈希表,它获取原始数据并在插入数据时产生哈希。不管怎样,散列仍然是数据,并且会有自己的散列码,散列表不会在意它不是原始数据。 【参考方案1】:在这种情况下,您根本没有真正使用哈希表——或者至少您所说的哈希没有被用作表的哈希。
换句话说,密码的哈希(例如,SHA-256)只是另一条数据,存储在表(哈希表或其他)中。仅当/当密码更改时才会修改。
它可能存储在在一个由用户名作为键的哈希表中——但如果是这样,您将在此处获得用户名,以便您可以在需要时重新哈希。
如果出于某种原因,您决定使用密码的安全散列作为表中的键,则完整的散列将 成为键,以及用于索引的内容该表将是该散列的某个散列(例如,两个字节的块全部异或在一起)。
编辑:就表大小调整(本身)而言,不,存储密钥不是绝对必要的。您只需存储原始哈希码的其余部分即可。换句话说,您通常会从生成 32 位散列开始。然后,您使用其中的一部分(但仅部分)索引到您的表中(例如,16 位)。当需要调整表的大小时,您将在表中的位置获取 16 位,并为其他 16 位存储散列的剩余部分以恢复原始的 32 位散列。假设您将表的大小增加一倍,那么您将使用 17 位作为索引,并将剩余的 15 位存储在表中。
如果您不介意对哈希本身有点疯狂,您也可以使用它来完全消除存储密钥的任何实际需要。例如,如果您从创建 256 位哈希(例如 SHA-256)开始,您可以使用 N 位作为哈希表的索引,而其余位则作为键。如果您的实际密钥长于 256 位,这将导致发生冲突的可能性——但与 SHA-256 的冲突非常罕见,以至于偶然遇到的几率几乎肯定低于在键比较,所以它说两个键是相同的,而实际上它们并不相同。
【讨论】:
虽然我同意你的说法,但它并没有回答我的问题。要调整哈希表的大小,我必须将键与值一起存储吗?【参考方案2】:短版:哈希表不能仅使用其数据的哈希码;它们必须包含实际数据。如果您提供给哈希表的只是一个哈希,那么就是数据。 散列散列表,这似乎是您试图创建的,在调整大小时不会有问题。就哈希表而言,您插入的哈希 是 数据,而产生数据的任何哈希都是无关紧要的。
加长版:
哈希表和哈希表是两个正交的概念。听起来您将两者混为一谈,在这两种情况下都没有多大意义。
对于哈希表,重点是将键明确映射到值。没有这两者,表就毫无用处。哈希表使用的哈希码不是,也不能在逻辑上与数据分离——它来自数据,并且通常是动态计算的。哈希码的唯一目的是快速定位(或存储)实际的 key->value 映射;你仍然需要一个实际的键和一个实际的值来映射。
需要实际密钥的主要原因是,哈希码仅此而已。在某种程度上,每个有用的哈希表和每个哈希函数都被pigeonhole principle绑定。这意味着
根据定义,每个哈希函数都可以为两个不同的值返回相等的哈希码;和 每个有用的哈希表都必须以某种方式解决冲突(相等的哈希码之间和/或指向同一存储桶的不同哈希码之间)。哈希表实际上受到鸽巢原理的双重影响;他们不仅必须将数据缩减为(通常为 int 大小的)哈希码,而且由于大多数哈希表无法合理地拥有 40 亿个桶,因此代码必须对该哈希码进行进一步缩减以将其转换为一个桶号。 (一个常见的例子是将哈希码乘以某个素数,mod 是桶的数量。)
即使是只读哈希表通常也必须解决冲突。想象一个只读哈希表,它使用了一个非常完美的哈希函数,表中的每个值最终都在自己的存储桶中。即使在这种情况下,如果您尝试查找不在表中的键,但会生成一个哈希码,该哈希码会解析为包含表中 键的存储桶,会发生什么情况?要么您需要对值本身进行仔细检查,要么表谎言并说密钥存在,即使哈希码实际上并不相等!
基本上,该表使用查找键的哈希码(或者,更典型地,该哈希码上某个函数的返回值)来确定要查找的存储桶,然后将该存储桶中的每个实际键与实际查找键以消除歧义。如果没有某种实际的键,这将无法工作,除非:
您的哈希函数保证为每个输入产生一个唯一值(使其不再是 hash 函数,而是 编码 或 加密 函数),和 您有无数个存储桶。在没有原始数据的情况下,您可以在哈希表中使用哈希的唯一方法是,如果哈希是数据。在这种情况下,您有一个 哈希哈希表。在您提到的情况下,您可以使用用户名的哈希作为密钥,映射到密码的 SHA /mcrypt/bcrypt/whatever 哈希。在这种情况下,散列 是 的关键,没有人关心用户名了。这不会导致调整大小或其他任何问题,因为哈希表使用的哈希函数与 you 使用的哈希函数几乎没有关系;就哈希表而言,您给它的哈希只是另一个值,并且有自己的哈希码,而那个哈希码是哈希表在内部用来查找内容的。
不过,我可能会警告您不要对用户名使用哈希值。我建议至少有一个 auth 数据是防碰撞的,而散列本质上是 not 防碰撞的。如果我发现你到处乱跑,将未散列的密码存储在潜在的用户可见位置,我会亲自撕毁你的编程许可证。 :) 也许如果您加密 用户名而不是散列它?这将有效地保证它的唯一性,如果你使用公钥加密并“忘记”私钥,它实际上就像散列一样不可逆转。很好的副作用是,由于它的工作方式,公钥加密通常有点慢。您基本上是在取一个 1024 位或更大的数字并将其自身相乘数千次。所以你有一些针对暴力破解的内置保护。
【讨论】:
【参考方案3】:您也可以通过存储原始密钥来做到这一点。对于密码,您不会将它们存储在哈希表中。
【讨论】:
【参考方案4】:我想这并不能回答您的问题,但我想知道哈希表如何完全不知道其条目的键。当然,除非您确定永远不会发生碰撞。在给定的键是用户名的示例中,我看不出这怎么可能(除非哈希表一开始就大得离谱)。
无论哪种方式,如果我们谈论的是一个实际的哈希表,而不是直接访问表,我认为在不知道键的情况下在不同大小的表之间复制条目在逻辑上是不可能的,因为通常是哈希表的大小是哈希函数的重要组成部分。
【讨论】:
以上是关于哈希表调整大小:我们如何在不知道密钥的情况下做到这一点?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不缩小图像的情况下在 Android 中调整图像大小?
如何在不破坏 CakePHP 中的 MVC 框架的情况下做到这一点?