哈希技术在广东电信公话200话单处理中的应用
本文发表于期刊《广东通信技术》2003年7期。
哈希技术在广东电信公话200话单处理中的应用
马根峰
(广东电信公用电话管理中心
广州 510635)
摘要 在电信领域,处理上亿条记录的大型话单文件是经常的。如何对大型文件按照一些指标(如电话号码,卡号)进行统计,这是一件相当复杂的工作。而哈希表是数据结构中的重要概念之一,由于它在记录查找时一次存取便能得到所查记录,所以在电信领域中对大型话单文件进行处理时,显示出相当高的效率。本文首先介绍了哈希表的有关知识,然后介绍了广东电信公用电话200话单处理中为了实现话单统计所采用的哈希表、冲突解决方法,接着介绍了话单处理的流程,最后简介了应用中的关键算法。
关键词 哈希表;哈希函数;冲突处理方法 ;关键算法
The application of Hash Table in statistics of client lose analyzing in Public Payphone Center, Guangdong Telecom Corporation
MA Gen-feng
(Public Payphone Center, Guangdong Telecom Corporation, Guangzhou 510635)
ABSTRACT: Hash Table is a important conception of data structure in computer field. Because it can get the record in one time’s read & write, it’s very efficient in the query of big table. Firstly the article introduces the interrelated knowledge to Hash Table, then introduces the Hash Table used and method of resolving conflict in the process of building the Hash Table in statistics of client lose analyzing in telecommunacation public payphone, then introduces the flow of uniting two data table in a application. Finally the key algorithm is introduced.
KEY WORDS: Hash Table ; Hash function ; method to resolving the conflict; key Algorithm
1
引言
公用电话业务是广东电信业务的一个重要部分,而200卡类业务则是广东电信公话中的一个举足轻重的业务,对200话务的分析因而显得非常重要。话务分析的依据是话机发生200业务的各项指标(如按本地、国内和国际流向的通话次数、计费时长和通话费用)和200卡发生的话务(卡属地通话费、卡异地通话费)。每个月如何对二十几GB的几十个话单文件中2至3亿条话单进行统计,形成几百万台话机使用200业务的情况和近二千万张200卡发生的话务,这是一件艰巨的工作。将话单文件直接入数据库进行处理是不可行的。在数据结构中有一个重要的概念,那就是哈希表,在解决这类问题上显示出卓越的效率。
笔者利用哈希技术,在PC机上使用DELPHI编写了两个程序。
其中的一个运行了50分钟就完成了对20多GB话单文件的处理,生成了全省三百多万部电话使用200业务的情况,然后用了十几分钟将话务信息入库;另一个运行了70分钟就完成了全省1900多万张200卡话务信息的统计,然后用了五十多分钟将统计结果入库。
2
哈希表
在折半查找、二叉树查找和B_树查找时,查找的效率依赖于查找过程中所进行的比较次数。而我们期望的情况是希望不经过任何比较,一次存取便能得到所查记录,那就必须在记录的存储位置和它的关键字之间建立一个确定的关系f,使每个关键字和结构中一个唯一的存储位置相对应。因而在查找时,只要根据这个对应关系f找到给定值K的像f(K)。若结构中存在关键字和K相等的记录,则必定在f(K)的存储位置上,由此,不需要进行比较便可直接取得所查记录。这个对应关系f就是哈希函数,按这个思想建立的表为哈希表。
3
使用哈希表来进行数据表的合并
3.1 哈希函数的选定
哈希表的构造方法很多,常用的方法包括直接定址法、数字分析法、平方取中法、折叠法、除数余数法和随机数法。其中除数余数法是种最简单,也最常用的构造哈希函数的方法。在这里我选用了除数余数法来构造哈希函数,将200话单中的话机号码或卡号转换成int64型,这个int64型整数除以一个大质数P即为该话机号码或卡号所对应的哈希值。
P值的选择: 在使用除数余数法时,对P值的选择很重要。若选的不好,容易产生哈希冲突。根据众人的经验,可以选P为质数或不包含小于20的质因数的合数。在统计广东省发生200业务的话机的话务应用和统计广东电信发行的200卡话务的应用中所采用的是寻找一个大质数P,并且P分别为稍大于广东电信各分公司固话数40%的质数、稍大于广东电信各分公司发行的200卡数量2倍的质数。这可以在哈希表类中增加一个函数来构造这个大质数P。
哈希表长度的确定:由于P大致为广东电信各分公司固话数30%的质数和广东电信各分公司发行的200卡数量2倍的质数,所以用P作为哈希表的长度可以有效地减少哈希冲突。
3.2 处理冲突的方法的选定
通常用的处理冲突的方法有下面几种,开放定址法、再哈希法、链地址法和建立一个公共溢出区法。在本应用中我采用的是链地址法。在链地址法中,将所有哈希值相同的记录存储在同一线性链表中,见下图1用链地址法处理冲突的哈希表示意。
而且采用链地址法时,查找成功时的平均查找长度Snc
和不成功时的长度Unc都比较小,
其中 a=表中填入的记录数/哈希表的长度,由于哈希表中填入的记录数据为一个分公司发行的200充值卡的数量,所以在这里
a<<0.5 。
3.3 利用哈希表进行200话单处理的流程
4
关键算法简介
4.1 哈希表类
type nodeType=record
szCallNumber:string[20]; // 主叫号码
…
next:pointer;
end;
linkSttp=pointer;
THash = class
intLenHash:integer;
intDataInHash:integer;
HashTbl :array of linksttp;
function isExistsInHash(strCallNumber:array of char;
var LPnode:linkSttp):integer;
constructor Create(int_p:integer);
//执行结束产生一个比int_p大的素数,这就是哈希表的长度intLenHash
function Add_node(p_200:prec_200); //prec_200为指向一条200话单的指针
destructor Destroy();
procedure saveToFile();
end;
4.2 在哈希表中增加一个结点
function THash.Add_node(p_200:prec_200);
begin
f_charge:=strToFloat(trim(p_200.szChargeTotal));
intCallNumber:=strToInt64(trim(copy(p_200^.szCallNumber,1,20)));
intMod:=intCallNumber mod intLenHash;
//调用isExistsInHash函数判断是否存在该卡号
localPoint:=nil;
i_return:=isExistsInHash(p_200^.szCallNumber,localPoint);
if i_return=0 then //存在该卡号且LPnode指向该哈希表中的结点
begin
… //修改哈希结点的值
end
else if i_return=1 then//不存在该卡号,并且hash[intmode]<>nil,
//localPoint指向hash[intMode]链表的最后一个结点
begin
new(p_temp);
p_temp^.szCallNumber :=copy(p_200^.szCallNumber,1,20);
localPoint^.next :=p_temp;
p_temp^.next:=nil;
… //设置哈希结点的值
end
else if i_return=2 then //不存在该卡号,并且hash[intMode]=nil, LPnode=nil
begin
new(p_temp);
p_temp^.szCallNumber :=copy(p_200^.szCallNumber ,1,20);
p_temp^.next:=nil;
HashTbl[intmod]:=p_temp;
… //设置哈希结点的值
end;
do_nextRecord:
end;
4.3判断是否存在结点strCallNumber
function THash.isExistsInHash(strCallNumber:array of char;
var LPnode:linkSttp):integer;
begin
intCallNumber:=strToInt64(trim(copy(strCallNumber,1,20)));
intMod:=intCallNumber mod intLenHash;
p_hash:=HashTbl[intMod];
if p_hash=nil then
//如果不存在该卡号,并且hash[intMode]=nil,则返回2,LPnode=nil
begin
LPnode:=nil;
isExistsInHash:=2;
end
else
begin
if p_hash^.szCallNumber=copy(strCallNumber,1,20) then
//如果存在该卡号,返回0,并且LPnode指向该哈希表中的结点
begin
LPnode:=p_hash;
isExistsInHash:=0;
end
else
begin
p_temp:=p_hash;
p_hash:=p_hash^.next;
while p_hash<>nil do
begin
if p_hash^.szCallNumber =copy(strCallNumber,1,20) then
//如果存在该卡号,返回0,并且LPnode指向该哈希表中的结点
begin
LPnode:=p_hash;
isExistsInHash:=0;
end;
p_temp:=p_temp^.next;
p_hash:=p_hash^.next ;
end;
LPnode:=p_temp;
isExistsInHash:=1;
end;
end;
end;
5
结束语
在电信业务领域内,对大型话单文件的处理是经常要进行的。而利用哈希表来进行记录的查找时,一次存取便能得到所查记录,显示出卓越的性能。关于这方面的问题,还需要和大家共同探讨。
参考文献:
1 严蔚敏,吴伟民.数据结构.北京:清华大学出版社,1992.6,253-263
2 (美)Steve Teixerira Xavier Pacheco 任旭钧,王永生,冯泽波等译.DELPHI 5 Developer’s Guide.北京:机械工业出版,2000.7,16-72