-
哈希函数
1
基本原理
我们使用一个下标范围比较
大的数组来存储元素。
可以设计一个函数
(哈希函数,
,
也叫做散列函数)
使得每个元素的关键字都与一个函数值(即数组下标)相对应,于是用这个数组单元来存储这个元素;也
可以简单的理解为,按照关键字为每一个元素
分
类
,然后将这个元素存储在相应
类
所对应的地方。
但是,不能够保证每个元素的关键字与函数值是一一对应的,因此极有可能出现对于不同
的元素,却
计算出了相同的函数值,这样就产生了
冲突
,换句话说,就是把不同的元素分在了相同的<
/p>
类
之中。后
p>
面我们将看到一种解决
冲突
的简便做法。
总的来说,<
/p>
直接定址
与
p>
解决冲突
是哈希表
的两大特点。
2
函数构造
构造函数的常用方法(下面为了叙述简洁,设
h(k)
表示关键字为
k
的元素所对应的函数值)
:
a)
除余法:
选择一个适当的正整数
p
,令
h(k ) = k mod
p
这里,
p
如果选取的是比较大的素数,
效果
比较好。
而且此法非常容易实现,
因此是最常用的方法。
b)
数字选择法:
如果关键字的位数比较
多,超过长整型范围而无法直接运算,可以选择其中数字分布比较均匀的若干
位,所组成
的新的值作为关键字或者直接作为函数值。
3
冲突处理
线性重新散列技术易于实现
且可以较好的达到目的。
令数组元素个数为
S
,
则当
h(k)
已经存储了元
素的时候,依次探查
(h(k)+i) mod S , i=1,2,3……
,
直到找到空的存储单元为止(或者从头到尾扫描一圈
仍未发现空单元,这就是哈希表已经
满了,发生了错误。当然这是可以通过扩大数组范围避免的)
。
4
支持运算
哈希表支持的运算主要有:初始化
(makenull)
、哈
希函数值的运算
(h(x))
、插入元素
(insert)
、查找元素
(member)
。
设插入的元素的关键字为
x
,
A
为存储的数组。
初始化比较容易,例如
const empty=maxlongint; //
用非常大的整数代表这个位置没有存储元素
p=9997;
//
表的大小
procedure
makenull;
var i:integer;
begin
for
i:=0 to p-1 do
A[i]:=empty;
End;
哈希函数值的运算根据函
数的不同而变化,例如除余法的一个例子:
function
h(x:longint):Integer;
begin
h:=
x mod p;
end;
我们注意到,插入和查找
首先都需要对这个元素定位,即如果这个元素若存在,它应该存储在什么位
置,因此加入
一个定位的函数
locate
function
locate(x:longint):integer;
var orig,i:integer;
begin
orig:=h(x);
i:=0;
while
(ix)and(A[(orig+i)mod
S]<>empty) do
inc(i);
//
当这
个循环停下来时,要么找到一个空的存储单元,要么找到这个元
//
素存储的单元,要么表已经满了
locate:=(orig+i) mod S;
end;
插入元素
procedure
insert(x:longint);
var posi:integer;
begin
posi:=locate(x);
//
定位函数的返回值
if A[posi]=empty then A[posi]:=x
else error;
//error
即为发生了错误,当然这是可以避免的
end;
查找元素是否已经在表中
procedure
member(x:longint):boolean;
var
posi:integer;
begin
posi:=locate(x);
if A[posi]=x then
member:=true
else
member:=false;
end;
这些就是建立在哈希表上的常用基本运算。
哈希表
基本概念
若结构中存在关键字和
p>
K
相等的记录,则必定在
f(K)
的存储位置上。由此,不需比较便可直接取得所
查记录。称这个对应关系
f
为散列函数
(Hash
function)
,按这个思想建立的表为散列表。
*
对不同的关键字可能得到同一散列
地址,即
key1≠key2
,而
f(
key1)=f(key2)
,这种现象称冲突。具
有相同函数
值的关键字对该散列函数来说称做同义词。
综上所述,
根据散列
函数
H(key)
和处理冲突的方法
将
一组关键字映象到一个有限的连续的地址集(区间)上,并以关键字在地址集中的
“
p>
象
”
作为记录在表中
的存储位置,这种表便称为散列表,这一映象过程称为散列造表或散列,所得的存储位置称散列地址。
经散列函数映象到地址集合中任何一个地址的概率是相等的,
*
若对于关键字集合中的任一个关键字,<
/p>
则称此类散列函数为均匀散列函数
(Uniform Hash
function)
,这就是使关键字经过散列函数得到一个
“
随机
的地址
”
,从而减少冲突。
常用的构造
散列函
数
的方法
....
< br>散列函数能使对一个数据序列的访问过程更加迅速有效,通过散列函数,数据元素将被更快地定位
1.
直接寻址法:取关键字或关键字的某个线性函数值为散列地址。
即
H(key)=key
或
H(key) = a?key + b
,其中
a
和
b
为常数(这种散列函数叫做自身函
数)
2.
数字分析法
3.
平方取中法
4.
折叠法
5.
随机数法
6.
除留余数法:取关键字被某个不大于散列表表长
m
的数
p>
p
除后所得的余数为散列地址。
即
H(key) = key
MOD p, p<=m
。不仅可以对关键字直接取模,也可在折叠、平方取中等运算之
后取
模。对
p
的选择很重要,一般取素
数或
m
,若
p
选的不好,容易产生同义词。
处理冲突的方法
1.
开放寻址法:
Hi=(H(key) + di) MOD
m, i=1,2,…, k(k<=m
-1)
,其中
H(key)
为散列函数,
m
< br>为散列
表长,
di
为增量序列,
可有下列三种取法:
1. di=1,2,3,…,
m
-1
,称线性探测再散列;
2.
di=1^2, (-1)^2, 2^2,(-
2)^2, (3)^2, …,
±(k)^2,(k<=m/2)
称二次探测再散列;
3.
di=
伪随机数序列,称伪随机探测再散列。
2.
再散列法:
Hi=RHi(key), i=1,2,…,k
RHi
均是不同的散列函数,即在同义词产生地址冲突时计算另
一个散列函数地址,直到冲突不再发生,这种方法不易产生
“
聚
集
”
,但增加了计算时间。
3.
链地址法
(
拉链法
)
4.
建立一个公共溢出区
查找的性能分析
散列表的查找过程基
本上和造表过程相同。一些关键码可通过散列函数转换的地址直接找到,另一些
关键码在
散列函数得到的地址上产生了冲突,需要按处理冲突的方法进行查找。在介绍的三种处理冲突的
< br>方法中,产生冲突后的查找仍然是给定值与关键码进行比较的过程。所以,对散列表查找效率的量度,依< /p>
然用平均查找长度来衡量。
查找过程中
,关键码的比较次数,取决于产生冲突的多少,产生的冲突少,查找效率就高,产生的冲
突多,查找效率就低。因此,影响产生冲突多少的因素,也就是影响查找效率的因素。影响产生冲突多少
有以下三个因素:
1.
散列函数是否均匀;
2.
处理冲突的方法;
3.
散列表的装填因子。
散列表的装填因子定义为:
α=
填入表中的元素个数
/
散列表的长度
由于表长是定值,
p>
所以,
α
是散列表装满程度的标志因子。<
/p>
α
与
“
填入表中
的元素个数
”
成正比,
α
越大,
填入表中的元素较多,产生冲突的可能性就越大;
α
越小,填入表中的元素较少,产生冲突的可能性就越
小。
实际上,散列表的平均查找长度是
装填因子
α
的函数,只是不同处理冲突的方法有不同的函数。<
/p>
了解了
hash
基本定义,就不能不提到一些著名的
hash
算法,
MD5
和
SHA-1
可以说是目前应用最
广泛
的
Hash
算法,而它们都是以
MD4
为基础设计的。那么他们都是什么意思呢
?
这里简单说一下:
(1)
MD4
MD4(RFC
1320)
是
MIT
的
Ronald L.
Rivest
在
1990
年设计的,
MD
是
Message Digest
的缩写。
它适用在
32
位字长的处理器上用高速软件实现
--
它是基于
32
位操作数的位操作来实现的。
(2)
MD5
MD5(RFC
1321)
是
Rivest
于
1991
年对
MD
4
的改进版本。它对输入仍以
512
位
分组,其输出是
4
个
32
位字的级联,与
MD4
相同。
MD5
比
MD4
来得复杂,并且速度较之要慢一点,但更安全,在抗分
析和抗差分方面表现更好
。
(3)
SHA-1
及其他
SHA1
是由
NIST NSA
设计为同
DSA
一起使用的,它对长度小于<
/p>
264
的输入,产生长度为
160bit
的
散列值,因此抗穷举
(brute-
force)
性更好。
SHA-1
设
计时基于和
MD4
相同原理
,
并且模仿了该算法。
那么这些
Hash
算法到底有什么用呢
?
Hash
算法在信息安全方面的应用主要体现在以下的
3
个方
面:
(1)
文件校验
我们比较熟悉的校验算法有
奇偶校验和
CRC
校验,这
2
种校验并没有抗数据篡改的能力,它们一定
程度上能检测并纠正数据传输
中的信道误码,但却不能防止对数据的恶意破坏。
MD5 H
ash
算法的
数字指纹
特性,使它成为目前应用最广泛的一种文件完整性校验和
(Checksum)
算法,不少
Unix
< br>系统有提供计算
md5
checksum
的命令。
(2)
数字签名
Hash
算法也是现代密码体系中的一个重要组成部分。由于
非对称算法的运算速度较慢,所以在数字
签名协议中,单向散列函数扮演了一个重要的角
色。对
Hash
值,又称
数字摘要
进行数字签名
,在统计
上可以认为与对文件本身进行数字签名是等效的。而且这样的协议还有其他的优
点。
(3)
鉴权协议
如下的鉴权协议又被称作挑
战
--
认证模式:在传输信道是可被侦听,但不可被篡改的情况
下,这是一
种简单而安全的方法。
M
D5
、
SHA1
的破解
2004
年
8
月
17
日,
在美国加州圣芭
芭拉召开的国际密码大会上,
山东大学王小云教授在国际会议上
首次宣布了她及她的研究小组近年来的研究成果
——
对
MD5
、
HAVAL
-
128
、
MD4
和
RIPEMD
等四个著
名密码算法
的破译结果。
次年二月宣布破解
SH
A-1
密码。
实际应用
以上就是一些关于
hash
以及其相关的一些基本预备知识。
那么
在
emule
里面他具体起到什么作用呢
?
大家都知道
emule
是基于
P2P
(
Peer-to-peer
的缩写,
指的是点对点的意思的软件)
,
它采
用了
多源文
件传输协议
”(MFTP
,
the Multisource
FileTransfer Protocol)
。在协议中,定义了一系列传输、压缩
和打包
还有积分的标准,
emule
对于每个文件都有
md5-hash
的算法设置,这使得该文件
独一无二,并且在整个
网络上都可以追踪得到。
什么是文件的
hash
值呢
?
MD5-Hash-
文件的数字文摘通过
< br>Hash
函数计算得到。不管文件长度如何,它的
Has
h
函数计算结果
是一个固定长度的数字。与加密算法不同,这一
个
Hash
算法是一个不可逆的单向函数。采用安全性高的
p>
Hash
算法,如
MD5
< br>、
SHA
时,两个不同的文件几乎不可能得到相同的
p>
Hash
结果。因此,一旦文件被修
改,就
可检测出来。
当我们的文件放到
em
ule
里面进行共享发布的时候,
emule
< br>会根据
hash
算法自动生成这个文件的
hash
值,他就是这个文件唯一的身份标志,它包含了这个文件的基本信息<
/p>
,
然后把它提交到所连接的服务
器。
p>
当有他人想对这个文件提出下载请求的时候,
这个
hash
值可以让他人知道他正在下载的文件是不是<
/p>
就是他所想要的。尤其是在文件的其他属性被更改之后(如名称等)这个值就更显得重要。
而且服务器还
提供了
,
这个文件当前所
在的用户的地址
,
端口等信息
,
这样
emule
就知道到哪里去下载了。
p>
一般来讲我们要搜索一个文件,
emul
e
在得到了这个信息后,会向被添加的服务器发出请求,要求得
到有相同
hash
值的文件。而服务器则返回持有这个文件的用
户信息。这样我们的客户端就可以直接的和
拥有那个文件的用户沟通,看看是不是可以从
他那里下载所需的文件。
对于
emu
le
中文件的
hash
值是固定的,也
是唯一的,它就相当于这个文件的信息摘要,无论这个文
件在谁的机器上,他的
hash
值都是不变的,无论过了多长时间,这个值始终如一,当我们在
进行文件的
下载上传过程中,
emule
都是通过这个值来确定文件。
那么什么是
< br>userhash
呢
?
道理同
上,当我们在第一次使用
emule
的时候,
< br>emule
会自动生成一个值,这个值也是唯一的,它是
我们在
emule
世界里面的标志,只要你不卸载,不删除
p>
config
,你的
userhash
p>
值也就永远不变,积分制
度就是通过这个值在起作用,
emule
里面的积分保存,身份识别,都是使用这个值,而和你的
id
和你的用
户名无关,你随便怎么改这些东西
,你的
userhash
值都是不变的,这也充分保证了公平性
。其实他也是
一个信息摘要,只不过保存的不是文件信息,而是我们每个人的信息。
p>
那么什么是
hash
文件呢
?
我们经常在
emule
日志里面看到,
emule
正在
hash
文件,这里就是利用了
hash
p>
算法的文件校验性
这个功能了,文章前面已经说了一些这些功能,其
实这部分是一个非常复杂的过程,目前在
ftp,bt
等软件<
/p>
里面都是用的这个基本原理,
emule
里面是采用文件分块传输,这样传输的每一块都要进行对比校验,如
果错误则要进行重新
下载,
这期间这些相关信息写入
met
文件,
直到整个任务完成,
这个时候
p
art
文件进
行重新命名,然后使用
m
ove
命令,把它传送到
incoming
文件里面,然后
met
文件自动删除,所以我们
有的时候会遇到
hash
文件失败,就是指的是<
/p>
met
里面的信息出了错误不能够和
pa
rt
文件匹配,另外有的
时候开机也要疯狂
hash
,有两种情况一种是你在第一次使用,这个时候要
hash
提取所有文件信息,还有
一种情况就是上一次你非法关
机,那么这个时候就是要进行排错校验了。
关于
hash
的算法研究,一直是信息科学里面的一个前沿,尤其在网络技术普及
的今天,他的重要性
越来越突出,其实我们每天在网上进行的信息交流安全验证,我们在
使用的操作系统密钥原理,里面都有
它的身影,特别对于那些研究信息安全有兴趣的朋友
,这更是一个打开信息世界的钥匙,他在
hack
世界
里面也是一个研究的焦点。
一般的线性表、树
中,记录在结构中的相对位置是随机的即和记录的关键字之间不存在确定的关系,
在结构
中查找记录时需进行一系列和关键字的比较。这一类查找方法建立在
“
< br>比较
”
的基础上,查找的效率
与
比较次数密切相关。理想的情况是能直接找到需要的记录,因此必须在记录的存储位置和它的关键字之
间建立一确定的对应关系
f
,使每个关键字和结构
中一个唯一的存储位置相对应。因而查找时,只需根据
这个对应关系
f
找到给定值
K
的像
f(K)
。若结构中存在关键字和
K
相等的记录,则必定在
f(K)
的存储位置
上,由此不需要进行比较便可直接取得所查记录。在此,称这个对应关系
f
为哈希函数,按这个思想建立
的表为哈希表(又称为杂凑法或散
列表)
。
哈希表不可避免冲突
(collision)
现象:对不同的关键字可能得到同一哈希地址
即
key1≠key2
,而
hash(key1)=hash(key2)
。具有相同函数值的关键字对该哈希函数来说称为同义词
(synonym)
。
因此,在
建造哈希表
时不仅要设定一个好的哈希函数,而且要设定一种处理冲突的方法。可如下描述哈希表:根据
设定的哈希函数
H(key)
和所选中的处理冲突的方法,
将一组关键字映象到一个有限的、
地址连续的地址集
(
区间
)
上并以关键字在
地址集中的
“
象
”
作为相应记录在表中的存储位置,这种表被称为哈希表。