This commit is contained in:
krahets
2024-04-06 03:02:26 +08:00
parent 5988d20958
commit a263b839b9
875 changed files with 484736 additions and 2893 deletions

View File

@ -26,7 +26,7 @@
<title>6.3 Hash Algorithm - Hello Algo</title>
<title>6.3 Hash algorithm - Hello Algo</title>
@ -153,7 +153,7 @@
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
6.3 Hash Algorithm
6.3 Hash algorithm
</span>
</div>
@ -201,7 +201,13 @@
<li class="md-select__item">
<a href="/" hreflang="zh" class="md-select__link">
中文
简体中文
</a>
</li>
<li class="md-select__item">
<a href="/zh-hant/" hreflang="zh-Hant" class="md-select__link">
繁體中文
</a>
</li>
@ -393,7 +399,7 @@
<span class="md-ellipsis">
0.1 About This Book
0.1 About this book
</span>
@ -414,7 +420,7 @@
<span class="md-ellipsis">
0.2 How to Read
0.2 How to read
</span>
@ -491,7 +497,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2m0 16H5V5h14v14M6.2 7.7h5v1.5h-5V7.7m6.8 8.1h5v1.5h-5v-1.5m0-2.6h5v1.5h-5v-1.5M8 18h1.5v-2h2v-1.5h-2v-2H8v2H6V16h2v2m6.1-7.1 1.4-1.4 1.4 1.4 1.1-1-1.4-1.4L18 7.1 16.9 6l-1.4 1.4L14.1 6 13 7.1l1.4 1.4L13 9.9l1.1 1Z"/></svg>
<span class="md-ellipsis">
Chapter 1. Introduction to Algorithms
Chapter 1. Introduction to algorithms
</span>
@ -507,7 +513,7 @@
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2">
<span class="md-nav__icon md-icon"></span>
Chapter 1. Introduction to Algorithms
Chapter 1. Introduction to algorithms
</label>
<ul class="md-nav__list" data-md-scrollfix>
@ -524,7 +530,7 @@
<span class="md-ellipsis">
1.1 Algorithms are Everywhere
1.1 Algorithms are everywhere
</span>
@ -545,7 +551,7 @@
<span class="md-ellipsis">
1.2 What is an Algorithm
1.2 What is an algorithm
</span>
@ -626,7 +632,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6 2h12v6l-4 4 4 4v6H6v-6l4-4-4-4V2m10 14.5-4-4-4 4V20h8v-3.5m-4-5 4-4V4H8v3.5l4 4M10 6h4v.75l-2 2-2-2V6Z"/></svg>
<span class="md-ellipsis">
Chapter 2. Complexity Analysis
Chapter 2. Complexity analysis
</span>
@ -642,7 +648,7 @@
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_3">
<span class="md-nav__icon md-icon"></span>
Chapter 2. Complexity Analysis
Chapter 2. Complexity analysis
</label>
<ul class="md-nav__list" data-md-scrollfix>
@ -659,7 +665,7 @@
<span class="md-ellipsis">
2.1 Algorithm Efficiency Assessment
2.1 Algorithm efficiency assessment
</span>
@ -680,7 +686,7 @@
<span class="md-ellipsis">
2.2 Iteration and Recursion
2.2 Iteration and recursion
</span>
@ -701,7 +707,7 @@
<span class="md-ellipsis">
2.3 Time Complexity
2.3 Time complexity
</span>
@ -722,7 +728,7 @@
<span class="md-ellipsis">
2.4 Space Complexity
2.4 Space complexity
</span>
@ -803,7 +809,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M11 13.5v8H3v-8h8m-2 2H5v4h4v-4M12 2l5.5 9h-11L12 2m0 3.86L10.08 9h3.84L12 5.86M17.5 13c2.5 0 4.5 2 4.5 4.5S20 22 17.5 22 13 20 13 17.5s2-4.5 4.5-4.5m0 2a2.5 2.5 0 0 0-2.5 2.5 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.5-2.5 2.5 2.5 0 0 0-2.5-2.5Z"/></svg>
<span class="md-ellipsis">
Chapter 3. Data Structures
Chapter 3. Data structures
</span>
@ -819,7 +825,7 @@
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Chapter 3. Data Structures
Chapter 3. Data structures
</label>
<ul class="md-nav__list" data-md-scrollfix>
@ -836,7 +842,7 @@
<span class="md-ellipsis">
3.1 Classification of Data Structures
3.1 Classification of data structures
</span>
@ -857,7 +863,7 @@
<span class="md-ellipsis">
3.2 Fundamental Data Types
3.2 Fundamental data types
</span>
@ -878,7 +884,7 @@
<span class="md-ellipsis">
3.3 Number Encoding *
3.3 Number encoding *
</span>
@ -899,7 +905,7 @@
<span class="md-ellipsis">
3.4 Character Encoding *
3.4 Character encoding *
</span>
@ -980,7 +986,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 5v14h17V5H3m4 2v2H5V7h2m-2 6v-2h2v2H5m0 2h2v2H5v-2m13 2H9v-2h9v2m0-4H9v-2h9v2m0-4H9V7h9v2Z"/></svg>
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Array and linked list
</span>
@ -996,7 +1002,7 @@
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Array and linked list
</label>
<ul class="md-nav__list" data-md-scrollfix>
@ -1034,7 +1040,7 @@
<span class="md-ellipsis">
4.2 Linked List
4.2 Linked list
</span>
@ -1076,7 +1082,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache
4.4 Memory and cache
</span>
@ -1155,7 +1161,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17.36 20.2v-5.38h1.79V22H3v-7.18h1.8v5.38h12.56M6.77 14.32l.37-1.76 8.79 1.85-.37 1.76-8.79-1.85m1.16-4.21.76-1.61 8.14 3.78-.76 1.62-8.14-3.79m2.26-3.99 1.15-1.38 6.9 5.76-1.15 1.37-6.9-5.75m4.45-4.25L20 9.08l-1.44 1.07-5.36-7.21 1.44-1.07M6.59 18.41v-1.8h8.98v1.8H6.59Z"/></svg>
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stack and queue
</span>
@ -1171,7 +1177,7 @@
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_6">
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stack and queue
</label>
<ul class="md-nav__list" data-md-scrollfix>
@ -1230,7 +1236,7 @@
<span class="md-ellipsis">
5.3 Double-ended Queue
5.3 Double-ended queue
</span>
@ -1311,7 +1317,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19.3 17.89c1.32-2.1.7-4.89-1.41-6.21a4.52 4.52 0 0 0-6.21 1.41C10.36 15.2 11 18 13.09 19.3c1.47.92 3.33.92 4.8 0L21 22.39 22.39 21l-3.09-3.11m-2-.62c-.98.98-2.56.97-3.54 0-.97-.98-.97-2.56.01-3.54.97-.97 2.55-.97 3.53 0 .96.99.95 2.57-.03 3.54h.03M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h5.81a6.3 6.3 0 0 1-1.31-2H5v-4h4.18c.16-.71.43-1.39.82-2H5V8h6v2.81a6.3 6.3 0 0 1 2-1.31V8h6v2a6.499 6.499 0 0 1 2 2V6a2 2 0 0 0-2-2Z"/></svg>
<span class="md-ellipsis">
Chapter 6. Hash Table
Chapter 6. Hash table
</span>
@ -1327,7 +1333,7 @@
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_7_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_7">
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hash Table
Chapter 6. Hash table
</label>
<ul class="md-nav__list" data-md-scrollfix>
@ -1344,7 +1350,7 @@
<span class="md-ellipsis">
6.1 Hash Table
6.1 Hash table
</span>
@ -1365,7 +1371,7 @@
<span class="md-ellipsis">
6.2 Hash Collision
6.2 Hash collision
</span>
@ -1395,7 +1401,7 @@
<span class="md-ellipsis">
6.3 Hash Algorithm
6.3 Hash algorithm
</span>
@ -1406,7 +1412,7 @@
<span class="md-ellipsis">
6.3 Hash Algorithm
6.3 Hash algorithm
</span>
@ -1430,7 +1436,7 @@
<li class="md-nav__item">
<a href="#631-goals-of-hash-algorithms" class="md-nav__link">
<span class="md-ellipsis">
6.3.1 &nbsp; Goals of Hash Algorithms
6.3.1 &nbsp; Goals of hash algorithms
</span>
</a>
@ -1439,7 +1445,7 @@
<li class="md-nav__item">
<a href="#632-design-of-hash-algorithms" class="md-nav__link">
<span class="md-ellipsis">
6.3.2 &nbsp; Design of Hash Algorithms
6.3.2 &nbsp; Design of hash algorithms
</span>
</a>
@ -1448,7 +1454,7 @@
<li class="md-nav__item">
<a href="#633-common-hash-algorithms" class="md-nav__link">
<span class="md-ellipsis">
6.3.3 &nbsp; Common Hash Algorithms
6.3.3 &nbsp; Common hash algorithms
</span>
</a>
@ -2028,7 +2034,7 @@
<li class="md-nav__item">
<a href="#631-goals-of-hash-algorithms" class="md-nav__link">
<span class="md-ellipsis">
6.3.1 &nbsp; Goals of Hash Algorithms
6.3.1 &nbsp; Goals of hash algorithms
</span>
</a>
@ -2037,7 +2043,7 @@
<li class="md-nav__item">
<a href="#632-design-of-hash-algorithms" class="md-nav__link">
<span class="md-ellipsis">
6.3.2 &nbsp; Design of Hash Algorithms
6.3.2 &nbsp; Design of hash algorithms
</span>
</a>
@ -2046,7 +2052,7 @@
<li class="md-nav__item">
<a href="#633-common-hash-algorithms" class="md-nav__link">
<span class="md-ellipsis">
6.3.3 &nbsp; Common Hash Algorithms
6.3.3 &nbsp; Common hash algorithms
</span>
</a>
@ -2088,43 +2094,43 @@
<!-- Page content -->
<h1 id="63-hash-algorithms">6.3 &nbsp; Hash Algorithms<a class="headerlink" href="#63-hash-algorithms" title="Permanent link">&para;</a></h1>
<h1 id="63-hash-algorithms">6.3 &nbsp; Hash algorithms<a class="headerlink" href="#63-hash-algorithms" title="Permanent link">&para;</a></h1>
<p>The previous two sections introduced the working principle of hash tables and the methods to handle hash collisions. However, both open addressing and chaining can <strong>only ensure that the hash table functions normally when collisions occur, but cannot reduce the frequency of hash collisions</strong>.</p>
<p>If hash collisions occur too frequently, the performance of the hash table will deteriorate drastically. As shown in the Figure 6-8 , for a chaining hash table, in the ideal case, the key-value pairs are evenly distributed across the buckets, achieving optimal query efficiency; in the worst case, all key-value pairs are stored in the same bucket, degrading the time complexity to <span class="arithmatex">\(O(n)\)</span>.</p>
<p><a class="glightbox" href="../hash_algorithm.assets/hash_collision_best_worst_condition.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Ideal and Worst Cases of Hash Collisions" class="animation-figure" src="../hash_algorithm.assets/hash_collision_best_worst_condition.png" /></a></p>
<p align="center"> Figure 6-8 &nbsp; Ideal and Worst Cases of Hash Collisions </p>
<p><a class="glightbox" href="../hash_algorithm.assets/hash_collision_best_worst_condition.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Ideal and worst cases of hash collisions" class="animation-figure" src="../hash_algorithm.assets/hash_collision_best_worst_condition.png" /></a></p>
<p align="center"> Figure 6-8 &nbsp; Ideal and worst cases of hash collisions </p>
<p><strong>The distribution of key-value pairs is determined by the hash function</strong>. Recalling the steps of calculating a hash function, first compute the hash value, then modulo it by the array length:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="nv">index</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>hash<span class="o">(</span>key<span class="o">)</span><span class="w"> </span>%<span class="w"> </span>capacity
</code></pre></div>
<p>Observing the above formula, when the hash table capacity <code>capacity</code> is fixed, <strong>the hash algorithm <code>hash()</code> determines the output value</strong>, thereby determining the distribution of key-value pairs in the hash table.</p>
<p>This means that, to reduce the probability of hash collisions, we should focus on the design of the hash algorithm <code>hash()</code>.</p>
<h2 id="631-goals-of-hash-algorithms">6.3.1 &nbsp; Goals of Hash Algorithms<a class="headerlink" href="#631-goals-of-hash-algorithms" title="Permanent link">&para;</a></h2>
<h2 id="631-goals-of-hash-algorithms">6.3.1 &nbsp; Goals of hash algorithms<a class="headerlink" href="#631-goals-of-hash-algorithms" title="Permanent link">&para;</a></h2>
<p>To achieve a "fast and stable" hash table data structure, hash algorithms should have the following characteristics:</p>
<ul>
<li><strong>Determinism</strong>: For the same input, the hash algorithm should always produce the same output. Only then can the hash table be reliable.</li>
<li><strong>High Efficiency</strong>: The process of computing the hash value should be fast enough. The smaller the computational overhead, the more practical the hash table.</li>
<li><strong>Uniform Distribution</strong>: The hash algorithm should ensure that key-value pairs are evenly distributed in the hash table. The more uniform the distribution, the lower the probability of hash collisions.</li>
<li><strong>High efficiency</strong>: The process of computing the hash value should be fast enough. The smaller the computational overhead, the more practical the hash table.</li>
<li><strong>Uniform distribution</strong>: The hash algorithm should ensure that key-value pairs are evenly distributed in the hash table. The more uniform the distribution, the lower the probability of hash collisions.</li>
</ul>
<p>In fact, hash algorithms are not only used to implement hash tables but are also widely applied in other fields.</p>
<ul>
<li><strong>Password Storage</strong>: To protect the security of user passwords, systems usually do not store the plaintext passwords but rather the hash values of the passwords. When a user enters a password, the system calculates the hash value of the input and compares it with the stored hash value. If they match, the password is considered correct.</li>
<li><strong>Data Integrity Check</strong>: The data sender can calculate the hash value of the data and send it along; the receiver can recalculate the hash value of the received data and compare it with the received hash value. If they match, the data is considered intact.</li>
<li><strong>Password storage</strong>: To protect the security of user passwords, systems usually do not store the plaintext passwords but rather the hash values of the passwords. When a user enters a password, the system calculates the hash value of the input and compares it with the stored hash value. If they match, the password is considered correct.</li>
<li><strong>Data integrity check</strong>: The data sender can calculate the hash value of the data and send it along; the receiver can recalculate the hash value of the received data and compare it with the received hash value. If they match, the data is considered intact.</li>
</ul>
<p>For cryptographic applications, to prevent reverse engineering such as deducing the original password from the hash value, hash algorithms need higher-level security features.</p>
<ul>
<li><strong>Unidirectionality</strong>: It should be impossible to deduce any information about the input data from the hash value.</li>
<li><strong>Collision Resistance</strong>: It should be extremely difficult to find two different inputs that produce the same hash value.</li>
<li><strong>Avalanche Effect</strong>: Minor changes in the input should lead to significant and unpredictable changes in the output.</li>
<li><strong>Collision resistance</strong>: It should be extremely difficult to find two different inputs that produce the same hash value.</li>
<li><strong>Avalanche effect</strong>: Minor changes in the input should lead to significant and unpredictable changes in the output.</li>
</ul>
<p>Note that <strong>"Uniform Distribution" and "Collision Resistance" are two separate concepts</strong>. Satisfying uniform distribution does not necessarily mean collision resistance. For example, under random input <code>key</code>, the hash function <code>key % 100</code> can produce a uniformly distributed output. However, this hash algorithm is too simple, and all <code>key</code> with the same last two digits will have the same output, making it easy to deduce a usable <code>key</code> from the hash value, thereby cracking the password.</p>
<h2 id="632-design-of-hash-algorithms">6.3.2 &nbsp; Design of Hash Algorithms<a class="headerlink" href="#632-design-of-hash-algorithms" title="Permanent link">&para;</a></h2>
<h2 id="632-design-of-hash-algorithms">6.3.2 &nbsp; Design of hash algorithms<a class="headerlink" href="#632-design-of-hash-algorithms" title="Permanent link">&para;</a></h2>
<p>The design of hash algorithms is a complex issue that requires consideration of many factors. However, for some less demanding scenarios, we can also design some simple hash algorithms.</p>
<ul>
<li><strong>Additive Hash</strong>: Add up the ASCII codes of each character in the input and use the total sum as the hash value.</li>
<li><strong>Multiplicative Hash</strong>: Utilize the non-correlation of multiplication, multiplying each round by a constant, accumulating the ASCII codes of each character into the hash value.</li>
<li><strong>XOR Hash</strong>: Accumulate the hash value by XORing each element of the input data.</li>
<li><strong>Rotating Hash</strong>: Accumulate the ASCII code of each character into a hash value, performing a rotation operation on the hash value before each accumulation.</li>
<li><strong>Additive hash</strong>: Add up the ASCII codes of each character in the input and use the total sum as the hash value.</li>
<li><strong>Multiplicative hash</strong>: Utilize the non-correlation of multiplication, multiplying each round by a constant, accumulating the ASCII codes of each character into the hash value.</li>
<li><strong>XOR hash</strong>: Accumulate the hash value by XORing each element of the input data.</li>
<li><strong>Rotating hash</strong>: Accumulate the ASCII code of each character into a hash value, performing a rotation operation on the hash value before each accumulation.</li>
</ul>
<div class="tabbed-set tabbed-alternate" data-tabs="1:14"><input checked="checked" id="__tabbed_1_1" name="__tabbed_1" type="radio" /><input id="__tabbed_1_2" name="__tabbed_1" type="radio" /><input id="__tabbed_1_3" name="__tabbed_1" type="radio" /><input id="__tabbed_1_4" name="__tabbed_1" type="radio" /><input id="__tabbed_1_5" name="__tabbed_1" type="radio" /><input id="__tabbed_1_6" name="__tabbed_1" type="radio" /><input id="__tabbed_1_7" name="__tabbed_1" type="radio" /><input id="__tabbed_1_8" name="__tabbed_1" type="radio" /><input id="__tabbed_1_9" name="__tabbed_1" type="radio" /><input id="__tabbed_1_10" name="__tabbed_1" type="radio" /><input id="__tabbed_1_11" name="__tabbed_1" type="radio" /><input id="__tabbed_1_12" name="__tabbed_1" type="radio" /><input id="__tabbed_1_13" name="__tabbed_1" type="radio" /><input id="__tabbed_1_14" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">Python</label><label for="__tabbed_1_2">C++</label><label for="__tabbed_1_3">Java</label><label for="__tabbed_1_4">C#</label><label for="__tabbed_1_5">Go</label><label for="__tabbed_1_6">Swift</label><label for="__tabbed_1_7">JS</label><label for="__tabbed_1_8">TS</label><label for="__tabbed_1_9">Dart</label><label for="__tabbed_1_10">Rust</label><label for="__tabbed_1_11">C</label><label for="__tabbed_1_12">Kotlin</label><label for="__tabbed_1_13">Ruby</label><label for="__tabbed_1_14">Zig</label></div>
<div class="tabbed-content">
@ -2693,7 +2699,7 @@
\]</div>
<p>It is worth noting that if the <code>key</code> is guaranteed to be randomly and uniformly distributed, then choosing a prime number or a composite number as the modulus can both produce uniformly distributed hash values. However, when the distribution of <code>key</code> has some periodicity, modulo a composite number is more likely to result in clustering.</p>
<p>In summary, we usually choose a prime number as the modulus, and this prime number should be large enough to eliminate periodic patterns as much as possible, enhancing the robustness of the hash algorithm.</p>
<h2 id="633-common-hash-algorithms">6.3.3 &nbsp; Common Hash Algorithms<a class="headerlink" href="#633-common-hash-algorithms" title="Permanent link">&para;</a></h2>
<h2 id="633-common-hash-algorithms">6.3.3 &nbsp; Common hash algorithms<a class="headerlink" href="#633-common-hash-algorithms" title="Permanent link">&para;</a></h2>
<p>It is not hard to see that the simple hash algorithms mentioned above are quite "fragile" and far from reaching the design goals of hash algorithms. For example, since addition and XOR obey the commutative law, additive hash and XOR hash cannot distinguish strings with the same content but in different order, which may exacerbate hash collisions and cause security issues.</p>
<p>In practice, we usually use some standard hash algorithms, such as MD5, SHA-1, SHA-2, and SHA-3. They can map input data of any length to a fixed-length hash value.</p>
<p>Over the past century, hash algorithms have been in a continuous process of upgrading and optimization. Some researchers strive to improve the performance of hash algorithms, while others, including hackers, are dedicated to finding security issues in hash algorithms. The Table 6-2 shows hash algorithms commonly used in practical applications.</p>
@ -2702,7 +2708,7 @@
<li>SHA-2 series, especially SHA-256, is one of the most secure hash algorithms to date, with no successful attacks reported, hence commonly used in various security applications and protocols.</li>
<li>SHA-3 has lower implementation costs and higher computational efficiency compared to SHA-2, but its current usage coverage is not as extensive as the SHA-2 series.</li>
</ul>
<p align="center"> Table 6-2 &nbsp; Common Hash Algorithms </p>
<p align="center"> Table 6-2 &nbsp; Common hash algorithms </p>
<div class="center-table">
<table>
@ -2754,7 +2760,7 @@
</tbody>
</table>
</div>
<h1 id="hash-values-in-data-structures">Hash Values in Data Structures<a class="headerlink" href="#hash-values-in-data-structures" title="Permanent link">&para;</a></h1>
<h1 id="hash-values-in-data-structures">Hash values in data structures<a class="headerlink" href="#hash-values-in-data-structures" title="Permanent link">&para;</a></h1>
<p>We know that the keys in a hash table can be of various data types such as integers, decimals, or strings. Programming languages usually provide built-in hash algorithms for these data types to calculate the bucket indices in the hash table. Taking Python as an example, we can use the <code>hash()</code> function to compute the hash values for various data types.</p>
<ul>
<li>The hash values of integers and booleans are their own values.</li>
@ -3016,7 +3022,7 @@ aria-label="Footer"
<a
href="../hash_collision/"
class="md-footer__link md-footer__link--prev"
aria-label="Previous: 6.2 Hash Collision"
aria-label="Previous: 6.2 Hash collision"
rel="prev"
>
<div class="md-footer__button md-icon">
@ -3028,7 +3034,7 @@ aria-label="Footer"
Previous
</span>
<div class="md-ellipsis">
6.2 Hash Collision
6.2 Hash collision
</div>
</div>
</a>
@ -3141,7 +3147,7 @@ aria-label="Footer"
<nav class="md-footer__inner md-grid" aria-label="Footer" >
<a href="../hash_collision/" class="md-footer__link md-footer__link--prev" aria-label="Previous: 6.2 Hash Collision">
<a href="../hash_collision/" class="md-footer__link md-footer__link--prev" aria-label="Previous: 6.2 Hash collision">
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
@ -3151,7 +3157,7 @@ aria-label="Footer"
Previous
</span>
<div class="md-ellipsis">
6.2 Hash Collision
6.2 Hash collision
</div>
</div>
</a>