您是否想了解密码学中的散列?如果你这样做了,那么你来对地方了。
在本文中,我们将探讨更多关于散列的信息。
散列是一种计算机科学技术,用于从一组对象或值中识别对象或值。
听起来很混乱?
让我们试着通过例子来理解。
好吧,学院和学校为每个学生提供一个唯一分配的号码。这个唯一的号码是用来识别学生和与他相关的信息的。用于生成唯一编号的方法是散列。
另一个流行的例子是图书馆,你会在书架上找到大量书籍。那里的每本书都有其唯一的识别号,因此可以在巨大的图书馆中找到它!
散列的一个现代示例是注册游戏的游戏玩家。Valorant 是 Riot 推出的一款免费游戏。免费游戏意味着数百万人将玩这款游戏。
使用散列算法生成的唯一标识值来标识每个玩家。
让我们尝试在下面更详细地理解它
什么是哈希?
如上所述,散列是从组中识别对象的方法。
每个对象在经过哈希处理后都会获得一个唯一的标识号。
但是,这在技术上意味着什么?
从技术上讲,数学函数从任何长度的任何输入字符串生成一个固定长度的输出。
比特币交易在交易获得唯一 ID 的地方进行哈希处理。
如果你输入“Hello, World!” 在SHA-256 散列算法中,您将获得以下输出:
输入:你好,世界!
输出:dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
这里 SHA256 从给定的输入生成输出。如您所见,我们使用了安全散列函数 (SHA-256) 散列算法。它是目前流行的散列方法之一,包括消息直通(MD5)和安全散列函数(SHA1)。
散列函数的关键属性使其可靠。让我们在下面列出它们。
- 确定性→这意味着在任何给定情况下,给定输入的输出都是相同的。
- Preimage Resistant → 抗原像特性确保哈希值对生成输入值没有用处。
- 计算效率高→哈希函数是高效的,不需要大量的计算资源来执行。
- 无法逆向工程→哈希函数无法逆向工程。
- 防碰撞→ 防碰撞确保没有两个输入导致相同的输出。
但是,如果您来这里是为了高级的东西,您不会失望的。
什么是哈希函数和哈希表?它们是如何工作的?
在本节中,我们将更详细地探讨哈希函数和哈希表。在散列方面,有散列函数。这些函数负责将大输入转换为小的固定输入。哈希表存储输出。
在散列过程中,对象根据它们的键/值对分布到数组中。因此,如果您将一个元素数组传递给散列函数,您将得到一个数组输出,其中每个元素现在都附加了一个键。键/值对在实时访问元素时非常有用,因为它提供了令人印象深刻的 O(1) 时间。
要实现散列函数,您可以取消两种首选方法。
- 第一种方法是使用散列函数将元素转换为整数。接下来,整数输出可用于在放入哈希表时访问元素。
- 另一个步骤是将元素放入哈希表中,然后使用哈希键检索它。
在第二种方法中,功能如下:
哈希 = hash_function(key) 索引 = 哈希 % array_size
在这里,散列和数组大小是相互独立的。索引值是根据数组大小计算的。模运算符(%)使我们能够计算该值。
简单来说,哈希函数可以定义为可以将任意大小的数据集映射到固定大小的数据集的函数。生成的固定大小的数据集可以存储在哈希表中。散列函数返回的值有许多名称。它们可以称为散列值、散列、散列和和散列码。
编写一个好的哈希函数
如果你想创建一个好的哈希函数或机制,你需要了解创建一个的基本要求。让我们在下面列出它们:
- 哈希函数需要易于计算。这意味着它不应该占用很多资源来执行。
- 哈希函数需要均匀分布。通过这样做,哈希表用于存储哈希值,这样就不会发生聚类。
- 最后一个要求是更少或根本没有碰撞。没有冲突意味着没有单个输出映射到两个输入。
从技术上讲,冲突是散列函数的一部分,它根本无法从散列函数中删除。目标是创建一个可以提供良好哈希表性能并通过冲突解决技术解决冲突的哈希函数。
为什么我们需要一个好的散列函数?
为了理解一个有用的散列函数的必要性,让我们看一个下面的例子。
假设我们要使用哈希技术创建一个哈希表,其中输入字符串如下所示,{“agk”、“kag”、“gak”、“akg”、“kga”、“gka”}
现在,我们创建一个散列函数,它简单地将 a(97)、g(103) 和 k(107) 的 ASCII 值相加,然后对总和乘以 307 取模。
显然,三个数之和也是 307。这意味着,如果我们对所有数进行置换,然后进行模运算,我们将得到相同的结果。最终结果是将所有字符串存储到相同的索引号。散列函数的算法时间也将是 O(n) 复杂度,这是不可取的。我们可以很容易地得出结论,我们描述的散列函数对于现实生活场景并不是最优的。
为了修复散列函数,我们可以部署将每个元素的 ASCII 值之和除以另一个素数 727。这样做,我们将为给定的输入字符串数组获得不同的输出。
学习哈希表
散列表在存储散列函数的结果时非常有用,散列函数计算索引,然后针对它存储一个值。最终结果将是具有 O(1) 复杂度的更快的计算过程。
哈希表传统上是解决需要 O(n) 时间的问题的好选择。
因此,如果您拿起一个固定长度的字符串,然后尝试学习该字符串的字符频率。
因此,如果 string = “aacddce”,则通用方法是多次遍历字符串并存储每个频率。
#提供一个输入字符串,并统计该字符串中字符出现的频率
#算法是0(n)复杂度时间
temp_list = [] 开始= “一个” str = "ababcddefff" def alpha_zeta (): 阿尔法 = 'a' 对于范围内的i ( 0 , 26 ): temp_list.append(阿尔法) alpha = chr ( ord (alpha) + 1 ) 返回temp_list temp_list = alpha_zeta() #print (temp_list) def character_frequency ( str , temp_list ): 对于temp_list中的每个: 频率 = 0 对于我在 str : 如果(我 == 每个): 频率 = 频率 + 1 打印(每个,频率) 字符频率(str ,temp_list)
上述程序的输出将如下所示:
a2 b 2 1 d 2 1 f 3 g 0 0 我 0 .. ..
现在,让我们用 C++ 实现一个哈希表并计算字符频率。
#include <iostream> 使用命名空间标准; 整数频率[26]; int hashFunc(char c) { 返回(c - 'a'); } 无效计数(字符串 S) { for (int i = 0; i< S.length(); ++i){ int index = hashFunc(S[i]); 频率[指数]++; } 对于 (int i = 0; i<26; ++i) { cout << (char)(i+'a') << ' ' << 频率[i] << endl; } } 主函数() { cout<<"你好世界"; countFre("abbaccbdd"); }
该程序的输出如下:
a2 b 3 2 d 2
与其他线性方法相比,该算法的 O(N) 复杂度使其更快。
如何解决冲突
有一些独特的方法可以解决哈希函数中的冲突。一种流行的方法是分离链接,也称为开放散列。它是用一个链表实现的,链表中的每个元素本身就是一个链表。这种方法可以存储元素并确保某些元素只是特定链表的一部分,从而解决冲突。这意味着没有两个输入值可以具有相同的输出哈希值。
在 Python 中探索哈希
在本节中,我们将快速了解 Python 中的 hash。我们选择Python的原因是它易于阅读,任何人都可以轻松使用。
由于散列是一个常用函数,它已经在 Python 库中实现。通过使用该模块,您可以提供一个对象作为其输入,然后返回散列值。
哈希方法的语法是:
哈希(对象)
如您所见,它接受一个参数,即对象。对象可以是整数、浮点数或字符串。
hash() 方法的返回值取决于输入。对于整数,它可能会返回相同的数字,而对于十进制和字符串则不同。
让我们看看下面的一些例子。
数 = 10 分贝= 1.23556 str1 = “尼什”
打印(哈希(数字)) 打印(哈希(十进制)) 打印(哈希(str1))
上述代码的输出如下:
但是,散列不能应用于所有对象类型。例如,如果你记得我们在第一个程序中创建了一个 a 到 z 的列表。如果我们尝试对其进行哈希处理,输出窗口将通过 TypeError: unhashable type: ‘list’
要将散列应用于对象列表,您需要使用元组。
元音 = ( 'a' , 'e' , 'i' , 'o' , 'u' ) 打印(哈希(元音)) 输出⇒ -5678652950122127926
密码学中的散列
此外,哈希在很长一段时间内一直是密码学的一部分。但是,散列的最佳用例是散列密码并存储它们。
默克尔树
Merkle 树是一种数据结构,在大型数据池中进行安全数据验证时非常有用。 在开放网络中存储和访问数据时,比特币和以太坊都利用 Merkle 树来解决许多技术障碍。
任何集中式网络都不必担心存储和访问数据,因为访问和存储数据只有一个来源。然而,当存在去中心化网络时,等式会发生变化,因为现在需要在数百个参与的对等方之间复制数据。
Merkle 树通过提供一种可信且有效的方式在对等点之间共享和验证数据来解决这个问题。

默克尔树示例
但是,我们为什么要在这里讨论 Merkle 树呢?Merkle 树使用哈希作为核心功能来连接不同的节点和数据块。
Merkle Trees 是一棵可以总结整个交易集的倒置树。
如果您想了解有关 Merkle 树以及它如何在密码学中使用散列的更多信息,请查看我们的详细指南:Merkle 树指南。在那里,我们讨论了 Merkle 树是如何在比特币和其他用例中实现的。
采矿过程
挖掘过程还利用了散列。在比特币挖矿方面,当有需求时,会在区块链中添加一个新区块。
需要遵循一种方法将块添加到区块链。当新块到达时,根据块的内容生成哈希值。此外,如果生成的哈希值超过网络难度,则开始将块添加到区块链的过程。
完成后,网络中的所有对等点都会确认添加了新块。
但是,这种情况很少发生,因为在大多数情况下,与生成的哈希相比,网络难度总是更高。还有一个方面在采矿过程中起着至关重要的作用。这是随机数。
nonce 被添加到块的哈希中,并且是一个任意字符串。完成后,将连接的字符串与难度级别进行比较。如果难度级别低于连接的字符串,则更改随机数,直到难度级别更高。
该过程可以概括为以下步骤:
- 每当生成或获取新块时,对内容进行散列以创建新的散列值,
- 生成一个新的 nonce 值并将其附加到散列中
- 散列过程发生在新的联系字符串上
- 然后将哈希的最终值与网络的难度级别进行比较
- 如果最终哈希值低于随机数,则再次重复该过程。该过程仅在哈希值大于随机数时停止。
- 一旦难度级别更高,块就会加入链
- 然后矿工负责挖掘新区块并在他们之间分享奖励。
“哈希率”一词也来自这里。哈希率是哈希操作发生的速率。更高的哈希率意味着矿工将需要更多的计算能力来参与挖掘过程。
结论
这导致我们在密码学深入指南中结束我们的散列。我们详细介绍了散列,并探讨了它背后的代码。
那么,你怎么看呢?在下面发表评论,让我们知道。
#常问问题
什么是密码学中的散列?
在密码学中,散列是一种使用有效方法将数据转换为唯一文本字符串的方法。此外,对数据类型或其大小没有限制——散列对所有数据都有效。
散列如何在密码学中使用?
密码学利用散列来散列密码或生成唯一标识号。