一、哈希说明
哈希算法是一种将任意长度的输入数据映射为固定长度的输出数据的算法。这个输出通常称为哈希值或哈希码。哈希算法的核心思想是将输入数据压缩成一个固定长度的哈希值,这个哈希值通常可以用来代表原始数据,用于快速的数据比较、查找、校验等。
如何工作
哈希算法的工作过程通常包括以下步骤:
- 接收输入:接收任意长度的输入数据,可以是文件、文本、图像等。
- 处理输入:哈希算法会将输入数据通过一系列复杂的计算,转换成一个固定长度的二进制字符串。
- 生成哈希值:通过处理后的数据,生成一个固定长度的哈希值,通常是一个数字或者一串十六进制字符。
哈希算法的特点
- 固定输出长度:无论输入数据的长度如何,哈希算法都会生成一个固定长度的哈希值。
- 唯一性:理想情况下,不同的输入数据应该生成不同的哈希值,避免产生冲突。
- 散列性:哈希算法应该能够将输入数据均匀地分布到哈希值的范围内,避免出现簇集现象。
- 不可逆性:从哈希值不能反推出原始数据,即使两个哈希值相同,也不能确定原始数据是否相同。
哈希算法的应用
- 数据校验:哈希算法可以用于校验数据的完整性,如文件完整性校验、消息完整性校验等。
- 密码存储:哈希算法可以将用户密码进行哈希处理后存储在数据库中,增加安全性。
- 散列表:哈希算法常用于实现散列表,提高数据的检索效率。
- 数字签名:哈希算法可以用于生成和验证数字签名,确保数据的真实性和完整性。
哈希算法的选择
在选择哈希算法时,需要考虑安全性、性能和碰撞率等因素。安全性是指算法是否能够抵御攻击,性能是指算法的计算速度,碰撞率是指哈希算法产生相同哈希值的可能性。根据具体的应用场景和需求,选择适合的哈希算法非常重要。
哈希算法广泛应用在信息安全、数据结构、数据库等领域。
二、C++代码示例
以下是六种散列表(哈希表)的C++实现,使用不同的散列(哈希)技术来处理键值。每种方法都具有独特的散列函数,这些函数用于计算存储在散列表中的元素的索引。我会实现一个简单的哈希表,其中包括基本的插入和搜索功能。
1. 直接定址法
在直接定址法中,键本身被用作数组的索引。这种方法的应用场景限制于键的范围比较小并且连续时。
#include <iostream>
#include <vector>
class DirectAddressTable {
private:
std::vector<int> table;
public:
DirectAddressTable(size_t size) {
table.resize(size, -1); // 使用-1表示空
}
void insert(int key) {
if (key < table.size()) {
table[key] = key; // 直接使用键作为索引
}
}
int search(int key) {
if (key < table.size()) {
return table[key];
}
return -1; // 未找到
}
};
int main() {
DirectAddressTable dat(100);
dat.insert(10);
std::cout << "Search key 10: " << dat.search(10) << std::endl;
return 0;
}
2. 数学分析法
数学分析法根据关键字的数学特性进行计算。这里用简单的模操作演示这种方法。
#include <iostream>
#include <vector>
class ModHash {
private:
std::vector<int> table;
int size;
int hashFunction(int key) {
return key % size; // 使用模运算作为散列函数
}
public:
ModHash(int size) : size(size) {
table.resize(size, -1);
}
void insert(int key) {
int index = hashFunction(key);
table[index] = key; // 存储键值
}
int search(int key) {
int index = hashFunction(key);
if (table[index] == key) {
return key;
}
return -1; // 未找到
}
};
int main() {
ModHash mh(10);
mh.insert(12);
std::cout << "Search key 12: " << mh.search(12) << std::endl;
return 0;
}
3. 平方取中法
平方取中法先将键值平方,然后取中间的几位数字作为索引。
#include <iostream>
#include <vector>
#include <cmath> // For pow()
class MidSquareHash {
private:
std::vector<int> table;
int size;
int hashFunction(int key) {
int squared = key * key;
int midDigits = (squared / 100) % size; // 取中间的几位作为索引
return midDigits;
}
public:
MidSquareHash(int size) : size(size) {
table.resize(size, -1);
}
void insert(int key) {
int index = hashFunction(key);
table[index] = key;
}
int search(int key) {
int index = hashFunction(key);
if (table[index] == key) {
return key;
}
return -1;
}
};
int main() {
MidSquareHash msh(10);
msh.insert(123);
std::cout << "Search key 123: " << msh.search(123) << std::endl;
return 0;
}
4. 折叠法
折叠法将键值分成若干部分,然后将这些部分相加以形成索引。
#include <iostream>
#include <vector>
#include <string> // For string manipulation
class FoldHash {
private:
std::vector<int> table;
int size;
int hashFunction(int key) {
std::string strKey = std::to_string(key);
int sum = 0;
for (char c : strKey) {
sum += c - '0';
}
return sum % size;
}
public:
FoldHash(int size) : size(size) {
table.resize(size, -1);
}
void insert(int
key) {
int index = hashFunction(key);
table[index] = key;
}
int search(int key) {
int index = hashFunction(key);
if (table[index] == key) {
return key;
}
return -1;
}
};
int main() {
FoldHash fh(10);
fh.insert(123456);
std::cout << "Search key 123456: " << fh.search(123456) << std::endl;
return 0;
}
5. 除留余数法
除留余数法是一种常用的哈希技术,使用键值与固定数的模运算确定索引。
#include <iostream>
#include <vector>
class ModulusHash {
private:
std::vector<int> table;
int divisor;
int hashFunction(int key) {
return key % divisor; // 使用除留余数法
}
public:
ModulusHash(int divisor) : divisor(divisor) {
table.resize(divisor, -1);
}
void insert(int key) {
int index = hashFunction(key);
table[index] = key;
}
int search(int key) {
int index = hashFunction(key);
if (table[index] == key) {
return key;
}
return -1;
}
};
int main() {
ModulusHash mh(10);
mh.insert(25);
std::cout << "Search key 25: " << mh.search(25) << std::endl;
return 0;
}
6. 随机数法
随机数法使用一个随机函数来确定索引。
#include <iostream>
#include <vector>
#include <random>
class RandomHash {
private:
std::vector<int> table;
int size;
std::default_random_engine engine;
std::uniform_int_distribution<int> distribution;
int hashFunction(int key) {
engine.seed(key); // Seed with key
return distribution(engine) % size; // Random index based on key
}
public:
RandomHash(int size) : size(size), distribution(0, size - 1) {
table.resize(size, -1);
}
void insert(int key) {
int index = hashFunction(key);
table[index] = key;
}
int search(int key) {
int index = hashFunction(key);
if (table[index] == key) {
return key;
}
return -1;
}
};
int main() {
RandomHash rh(10);
rh.insert(998);
std::cout << "Search key 998: " << rh.search(998) << std::endl;
return 0;
}
三、后论
每种哈希方法都有其适用场景和优缺点,选择哪种方法取决于具体需求,例如键的分布、表的大小以及对冲突处理的需求。在实际应用中,这些因素都应该考虑在内。
哈希理解即可,使用一些著名的hash算法即可(一般都有开源库实现),一些场景也可以简单实现。
诸如:
- MD5(Message Digest Algorithm 5):是常见的哈希函数,它生成128位(16字节)的哈希值,通常用于校验数据完整性,但由于其安全性已经被攻破,不推荐用于安全目的。
- SHA-1(Secure Hash Algorithm 1):产生160位(20字节)哈希值的算法,常用于数字签名等安全应用。然而,由于其碰撞攻击已经被发现,被认为不再安全。
- SHA-256/SHA-512(Secure Hash Algorithm 256/512):SHA-256和SHA-512是SHA-1的后续版本,分别产生256位和512位的哈希值。它们目前被广泛使用,并被认为是相对较安全的哈希算法。
- CRC32(Cyclic Redundancy Check 32):CRC32是一种循环冗余校验算法,通常用于快速检测数据在传输过程中的错误。它生成32位的哈希值,适用于非安全性的数据完整性校验。
- MurmurHash:MurmurHash是一系列快速、低碰撞的非加密哈希函数,设计用于散列表等应用。它具有良好的性能和分布特性。
- CityHash:CityHash是Google开发的一种哈希函数,适用于非安全性的哈希需求。它具有较快的速度和低碰撞率。
the end~