Hash查找法在Keil C51中的实现
摘要:散列(hash)是一种重要的存储方法,也是一种常见的查找方法。它是指在记录的存储位置和它的关键字之间建立一个确定的对应关系。本文以射频卡门禁控制器为例,说明用射频卡卡号作为关键字,用Hash查找法确定此卡能否开门,并给出对应的Keil C51程序。
单片机应用系统中,经常要涉及到数据的存储和查找。以射频卡门禁系统为例,见图1。系统由51系列单片机、射频卡(RF卡)读卡电路、存储单元24C256、继电器等部分组成。其基本原理为:用户刷卡后,RF卡读卡电路读出卡号,通过I/O口线送入单片机。单片机收到卡号后,在存储单元中查找此卡是否允许开门。如允许,则命令继电器动作,打开电磁门锁:如不允许,则返回。
图1 射频卡门禁系统
在内存中查找卡号有多种方法,最简单的有线性查找法,即从存储单元内第一个记录起与关键字比较,一直到记录与关键字匹配。时间复杂程度为O(n),记录数越多,比较过程耗时越长。一般记录数为100~200时较合适,如在系统内存储1000~2 000条记录,则用户在刷卡开门时,因比较的次数较多,等待时间较长,将难以忍受。
对于记录数较多(1000~2 000)的场合,可以采用对分法查找。此方法的时间复杂程度为O(log2n),2000个左右的记录只需查找10~11次即可完成,效率大大提高。只是此法需要将记录事先排序,随机增加一个记录或养活一个记录将较麻烦。
二叉排序树的方法可以兼有对分法查找的高效率和随机插入记录、删除记录的便利。但在编程中,由于要使用到指针变量,KeilC51要生成较大的目标代码。
Hash查找法在实践中被证实是最快的一种查找方法,它的时间复杂度为O(1),即几乎只要比较一次就可确定比较结果。它的基本思想是以空
间换时间,需要数据存储器容量大,好在当前存储器的价格并不贵,采用大容量的存储并不会使系统成本增加多少。
Hash查找法又称散列查找,是一种重要的存储和查找方法。它是指在记录的存储位置和它的关键字之间建立一个确定的对应关系,使每个关
键字和存储器中的唯一的存储位置相对应。将记录的关键字与记录的存储位置对应起来的关系称为Hash函数。在查找时,如关键字是Key,只需要根据对应关系计算出关键字Key对应的值H(Key),就可得到记录的存储位置,这个位置称为Hash地址。以射频卡门禁系统为例,开门卡的卡
号为关键字,通过文中给定的一种运算即可直接得到对应的记录的存储位置(Hash地址),从中取出是否可以开门的信息。
使用Hash查找法,会产生对于不同的关键字其Hash函数计算的Hash地址相同的情况,这种情况称为冲突。在Hash查找法中冲突是不可避
免的。关键的问题是如何构造Hash函数,使所有关键字对应的Hash地址均匀地分布在整个地址空间,尽可能少地减少活冲突。同时一旦发生冲
突要有足够的办法解决。本文采用折叠法来构成Hash函数,用线性探测法解决一旦发生的冲突。其细节可见参考文献[1]、[2]。
当前单片机应用的普遍趋势是采用片内ROM,采用SPI或I2C等串行方式扩展外部设备,所以文中采用的存储器是I2C总线的24C256,共32K字节。其中分配给Hash查找的存储空间是16K,每记录8个字节,共2000条记录,即可存储2000个开门卡卡号。(24C256中剩余的地址留作它用)每条记录分配如下:
0 1 2 3 4 5 6 7
55 16 92 64 02 XX XX
第0个字节0X55,表示该地址已有记录。
第1个字节到第4个字节存放后8位卡号,BCD方式。
第5个字节~第7个字节留作参数,如可控制开门卡在什么时间段内可以开门,也可控制该卡不同的权限。
记录从0000号单元开始存放。
卡号存放在数组d[0]~d[9]中,它是一个10位的10进制数,如卡号是0016926402,则d[0]=0,d[1]=0,d[2]=1,d[3]=6,d[4]=9……。折叠法把关键字分割成位数相同的几部分,然后取这几部分叠加其和再取2000的模(舍去进位)作为哈希地址。
1692
+ 6402
————H(key)=94
8094
程序中作如下运算
1000 d[2]+100*d[3]+10*d[4]+d[5]+1000*d[6]+100*d[7]+10* [8]+d[9]=1000*(d[2]+d[6])+100*(d[3]+d[7])+10*(d[4]+d[8])+d[5]+d[9]这样的运算体现在hashfunc()函数中,hashfunc()函数的功能为根据10位卡号计算出对应的hash地址。
uint hashfunc()
{
uint hashtem;
hashtem=*(d[]+d[])+*(d[]+d[])+*(d[]+d[]+d[]+d[];
hashtem =hashtem%;
retun(hashtem);
}
本文所附C51程序中要用到其他一些函数,限于篇幅,这里不再申明,请参考main()程序中相应的注释。程序是以位变量setflag控制程序分支,setflag=1表明要将读到的卡号存储(函数save())到相应的hash地址中,setflag=0表明要将读到的卡号作为关键字,在内存中进行hash查找,找到相匹配的纪录。KeilC51程序如下:
Main()
{
uint hashaddr,IICaddr;
uchar status,i,k,cardmen,cardnum,seterr;
reterr=;
start:
Rfread(); //读卡,卡号存d[0]-d[9]
Setflag=checkcard(); //检测是否是设置卡,是设置卡返回1,是开门卡返回0。
)
{
k=;
hashaddr=hashfunc();//对关键字进行Hashing运算,得到Hashing地址。
)
{
IICaddr=(hashaddr+k)*;//从Hashing地址换算到实际内存地址。
Status=IICRead(IICaddr);
if(status==0x55)
{
;i<;i++)
{
cardmen=IICRead(IICaddr+i);//取出内存中卡号。
cardnum=d[i*]*+d[+i*];//与当前的卡号比较,
if(cardmen!=cardnum)
break;//
}
)
{
open();//开门3秒
goto start;
}
}
k++;//进行10次探测。
}
}
else
{
k=;
hashaddr=hashfunc();//对关键字进行Hashing运算,得到Hashing地址。
)。
//进行10次线性探测,10次不成功.不再进行探测,令错误标记errflag=1
{
IICaddr=(hashaddr+k)*;//从Hashing地址换算到实际内存地址
status=IICRead(IICaddr);
if(status==0x55)
{
;i<;i++)
{
cardmem=IICRead(IICaddr+i);
cardnum=d[i*]*+d[+i*];
if(eardmem!=cardnum)
break;
)
goto start;//内存中已有本卡的纪录,不须再写入。
k++;
}
else
{
)
save(IICaddr);//将一条记录存入。
;
goto start;
}
}
}
}
参考文献:
[1] 严蔚敏,吴伟民.数据结构(C语言版)[M].北京:清华大学出版社.
[2] 李云清.数据结构(C语言版)[M].北京:人民邮电出版社.
[3] 马忠梅. 单片机的C语言应用程序设计[M].北京:北京航空航天大学出版社.
Hash查找法在Keil C51中的实现的更多相关文章
- KEIL C51 中嵌入汇编以及C51与A51间的相互调用
如何在 KEIL C51(v6.21) 中调用汇编函数的一个示例 有关c51调用汇编的方法已经有很多帖子讲到,但是一般只讲要点,很少有对整个过程作详细描述,对于初学者是不够的,这里笔者通过一个简单例子 ...
- Keil C51中变量的使用
引言 8051内核单片机是一种通用单片机,在国内占有较大的市场份额.在将C语言用于51内核单片机的研究方面,Keil公司做得最为成功.由于51内核单片机的存储结构的特殊性,Keil C51中变量的使用 ...
- Keil C51中函数指针的使用
函数指针在C语言中应用较为灵活.在单片机系统中,嵌入式操作系统.文件系统和网络协议栈等一些较为复杂的应用都大量地使用了函数指针.Keil公司推出的C51编译器是事实上80C51 C编程的工业标准,它针 ...
- Keil C51 中指针的使用
指针是C语言中比较难的一个内容,Keil C51在指针方面有和标准C不一样的地方,今天看了一些资料学习了一下Keil C51 中指针的使用. keil51的指针,包含两种指针:普通指针,兼容标准C:内 ...
- 关于Keil C51中using关键字的使用心得
刚才看到一位很牛的师兄写的一篇日志中提到了Keil C51中using这个关键字的用法,粗心的我本来一直都没有留意它是用来干嘛的(因为我一般看见它都是在中断服务函数的定义开头处,好像没有了它也可以中断 ...
- [51单片机] Keil C51中变量的使用方法详解
引言 8051内核单片机是一种通用单片机,在国内占有较大的市场份额.在将C语言用于51内核单片机的研究方面,Keil公司做得最为成功.由于51内核单片机的存储结构的特殊性,Keil C51中变量 ...
- KEIL C51中const和code的使用
code是KEIL C51 扩展的关键字,用code修饰的变量将会被放到CODE区里.但C语里的const关键字好像也有定义不能改变的变量的功能,这两个关键字有什么区别呢?在帮助手册里查找const, ...
- Keil C51 中的函数指针和再入函数
函数指针是C语言中几个难点之一.由于8051的C编译器的独特要求,函数指针和再入函数有更多的挑战需要克服.主要由于函数变量的传递.典型的(绝大部分8051芯片)函数变量通过堆栈的入栈和出栈命令来传递. ...
- keil c51中C程序的启动过程
汇编是从org 0000h开始启动,那么keil c51是如何启动main()函数的?keil c51有一个启动程序startup.a51,它总是和c程序一起编译和链接.下面看看它和main()函数是 ...
随机推荐
- 关于openoffice英文乱码的问题
首先选中乱码的部分,然后在右边的侧栏中看到其字体,尝试改变它的字体,看会不会显示正常,如果可以,先记住这两种字体.然后: 工具->选项->字体 然后在使用替换表打上勾, ...
- Stage3D_Game_Programming:渲染3D模型
OBJ是文件,先来解释下OBJ文件.随便找一个OBJ文件,用文本查看: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # ...
- 设计模式21---设计模式之享元模式(Flyweight)(结构型)
1.讲解享元模式(结构型) 1.1享元模式定义 运用共享技术有效地支持大量细粒度对象. 享元:把内部状态共享出来 1.2享元模式要点 重点在于分离变与不变. 把一个对象的状态分为内部状态和外部状态,内 ...
- RHEL6.4编译安装gcc4.8.1
因为平时用的linux虚拟机都是RHEL6.4, 自带的gcc版本号太低,不支持C++11,而从gcc4.8.1開始, gcc完美支持C++11全部特性. 故升级了一下. 以下是具体步骤: 1. 下载 ...
- Tomcat中Listener的使用范例(转载http://cywhoyi.iteye.com/blog/2075848)
Tomcat是非常有名的开源容器,因其开源我们可以对其做定制化的改变,而且Tomcat在其配置文件方面做了很多注释说明摘要,帮助我们更好的定制化我们所需的功能点. New Tomcat Listene ...
- Android(java)学习笔记248:ContentProvider使用之虚拟短信
1.虚拟短信应用场景: 急着脱身?应付老婆(老公.男女朋友查岗)? 使用虚拟通话短信吧.您只需通过简单设置,软件就会在指定时间会模拟一个“真实”来电或短信来迷惑对方,通过“真实”的证据让对方相 ...
- OSX10.11 CocoaPods 升级总结
本文不会讨论CocoaPods的各种使用技巧以及各种原理,只是简单记录一下在升级过程中遇到的问题,如果使用中有各种问题来欢迎交流. Podfile.loc 文件变化 前几天一个小伙更新了CocoaPo ...
- Could not fetch https://api.github.com/repos/RobinHerbots/jquery
使用 composer 安装YII2时, 如题所示提示, 原因是由于yii安装中, 需要有一些相关的认证[或许说是composer的认证], 如有如下提示 Could not fetch https: ...
- python-Pickle序列化
[Python之旅]第三篇(二):Pickle序列化 python 序列化 pickle 摘要: 说明:关于Pickle的说明 作如下说明: 1 2 3 4 5 6 7 序列化的概念很简单 ...
- Have trouble in your life
当你烦恼的时候不知道如何是好时,你可以下载此程序,可以帮助你化解烦恼! 下载地址: http://pan.baidu.com/s/1i3FtxHF