![]() |
|
||||||
1. 概述
通用水文资料整编程序是上世纪80年代用FORTRAN77编写的,限于当时的计算机硬件、软件发展水平,该程序编程工作量较大,基本上没有进行数据结构设计,程序结构混乱难读,不易维护和升级。目前,计算机软硬件技术已发展到相当高的水平,开发数据结构严谨、程序结构清晰、算法简洁易读的软件已不存在困难。本文的数据结构及算法描述采用Pascal语言进行描述,限于篇幅,作者只对降水资料整编程序的关键技术进行论述。
2. 需求分析
根据《水文资料整编规范》,降水部分需要完成:逐日降水量表、降水量摘录表、各时段最大降水量表(1)、各时段最大降水量表(2)四个整编项目,因此整编程序必须具备日月年降水量统计、不同段制雨强的降水量摘录以及对降水序列按不同分钟、小时数的滑动统计功能。
3.数据结构分析及定义
数据结构是指数据对象极其相互关系和构造方法,按逻辑关系可分为线性结构和非线性结构两类;数据结构的定义直接关系到算法的种类和实现难度。为便于实现整编程序的各项功能,又能设计出简洁明了的算法,需要对降水的原始数据、运行期数据等进行详细的分析,确定最佳数据结构设计方案。
3.1 原始数据
降水原始数据是按时段降水量进行整理的,时段降水量包括:降水开始时间、结束时间、降水量、降水物、整编符号五个元素,这个五个元素组成一个时段降水量记录。时段降水量在时序上是连续、完整的,年降水量有n个时段降水量记录组成。另外,降水时间记录以分钟进行统计,而降水原始记录的开始结束时间是月日时分组成的字符串,为便于对降水时间进行处理,需定义时间记录,从该记录中可以直接得到分解的月、日、时、分及完整的时间(计算机操作系统中,时间实际上是一个实型数值,该数值始于1899-12-31日,以日为单位,如1900-01-01的值为2,因此,利用时间的这个属性很容易实现降水量统计)。
(1)时间记录数据结构
Type
Ttzytimerec =record
Year,Mon,Day,Hour,Min:integer; //分解的 年.月.日.时.分记录
Time:real;//完整时间
End;
(2)时段降水量数据结构
Type
TsegPRec=record
BeginTime,EndTime:Ttzytimerec;//时段的开始、结束时间记录
Pseg:real;//时段降水量
Pw,Ps:string;//降水物、整编符
End;
(3)年降水量记录数据结构
Type
TyearPRec_sou=record
Segnum:integer;//时段降水量记录的数量
SegPRec:array of TsegPRec;//年时段降水量集合
End;
说明:Recnum时段降水量记录的数量是动态的,不同站年其数量是变化的,具体数值由实际数量确定,程序根据实际数量开辟内存空间,动态内存管理。原通用程序采用静态数组定义,定义的太大会浪费内存,太小则可能造成空间不足,程序无法运行;本解决方案采用动态数组,完全避免了这些弊端。
3.2 运行期数据结构定义
由于最大降水量表(1)要求以分钟为单位对降水量进行统计、最大降水量表(2)要求以小时为单位对降水量进行统计、逐日降水量表则要求以日月年为单位进行统计。因此在程序运行期间,需要创建分钟、小时、日、月、年五种数据结构存储各自的降水量以方便统计。
(1)分钟降水量数据结构
type
TMinPrec=record
Pmin:real;//分钟降水量
Pw,Ps:string;//该分钟内的降水物、整编符
end;
(2)小时降水量数据结构
小时降水量由该小时内60分钟的降水量记录组成,数据结构定义如下:
type
THourPrec=record
PHour:real;//小时降水量总量
Pw,Ps:string;//该小时内的降水物、整编符
minPrec:Array[0..59] of TminPrec;//小时内60分钟的降水量记录
end;
(3)日降水量数据结构
日降水量由该日内24小时的降水量组成,数据结构定义如下:
type
TDayPrec=record
Pday:real;//日降水总量
Pw,Ps:string;//日内降水物、整编符
HourPrec:Array[0..23] of THourPrec;//日内24小时的降水量记录
end;
(4)月降水量数据结构
月降水量由该月内各日的降水量组成,数据结构定义如下:
type
TMonPrec=record
Daynum:integer;//月内日的数量,值由年份、月份决定动态变化
PMon:real;//月降水总量
DayPrec:Array of TDayPrec;//月内各日的降水量记录,数量为Daynum
end;
(5)年降水量数据结构
年降水量由12个月的降水量记录组成,数据结构定义如下:
type
TyearPrec_rlt=record
PYear:real;//年降水总量
MonPrec:Array[0..11] of TMonPrec;//年内12个月的降水量记录
end;
由于已经存在存储原始数据的年数据结构,为方便程序设计和算法实现,需要将两个数据结构进行合并,合并后的数据结构定义如下:
type
TyearPrec=record
Segnum:integer;//原始数据中时段降水量记录的数量
SegPRec:array of TsegPRec;//原始数据中,年内Segnum个时段的降水量集合
PYear:real; //存储统计的年降水总量
MonPrec:Array[0..11] of TMonPrec;//存储各月(含日、时、分钟)的降水量记录
end;
年数据结构TyearPrec即可以存储年内各时段的原始数据,也可以存储年内月、日、时、分的统计数据,为方便理解年存储结构的原理,绘制年数据结构在内存中的影象图,见图1。
![]() 图1 降水年数据结构内存影象图
从图一可以看出,年数据结构TyearPrec是一个图型数据结构,有一个头指针HPyprec,即年数据在内存中的首地址,通过它可以访问年降水原始数据任何一个时段的记录和某月、日、时、分的降水记录。如访问第n个降水时段的降水量,可以通过HPyprec. SegPRec[n]. Pseg获得;又如访问7月21日8时59分的降水量可以通过HPyprec. MonPrec[7]. DayPrec[21]. hourPrec[8]. MinPrec[59]获得。可以发现,通过这种年数据结构,可以方便的进行年月日时分降水量的统计,用这种数据结构实现逐日降水量表的制作非常方便。但是用这种数据结构很难实现最大降水量表(1)和表(2)的制作,对于表(1),需要按分钟进行滑动统计,而在这种数据结构中不同小时的分钟数据在地址上是不连续的,同样对于表(2)要求的按小时数据在地址上也不连续,对分钟、小时数据的访问都需要通过月日记录进行访问,所以要实现对小时、分钟数据的连续访问,需要对现有数据结构进行改造。
改造方案如下:由于分钟数据记录、小时数据记录在内存中都已存在,如果再单独创建新的数据结构存放分钟、小时的降水数据记录,就会浪费内存。对操作系统比较熟悉的人员都知道在内存中的任何数据都有唯一的地址,因此可以创建两个只存放地址的动态链表(指针),一个连表存放分钟记录的地址,另一个存放小时记录的地址;这样就可以通过两个指针连表实现对分钟、小时数据记录的连续访问,而又最大限度的节省了内存的使用量。
(6)存储小时数据记录地址的指针链表数据结构
type
PPhourec=^PHour;
PHour =record
hourec:TPhourrec;//当前小时数据记录的地址
next:PPhourec; //下一小时数据记录的地址
end;
(7)存储分钟数据记录地址的指针链表数据结构
type
PPminrec=^pmin;
pmin=record
minrec:TPminrec; ;//当前分钟数据记录的地址
next:PPminrec; //下一分钟数据记录的地址
end;
创建两个指针链表后,需要在年数据结构中增加两个指针记录,分别存储分钟、小时链表的头指针地址。年数据结构经过改造后,其内存影象可以用图二表示。从图中可以看出,可以通过三种途径实现对小时、分钟降水记录的访问,第一种在前面已作说明,另外两种是通过小时、分钟链表的头指针实现对小时和分钟记录的连续访问。
![]() 图2 改造后的年降水数据结构内存影象图
从图二可以看出改良后的数据结构增加了两个头指针(建立在年数据结构内部),小时头指针将所有的小时记录串联起来,分钟头指针将所有的分钟数据记录串联起来,通过这两个指针链表可以方便的实现对分钟、小时数据记录的访问。但是,通过指针链表访问还存在一个问题,即无法确定链表中的小时记录属于哪一天,也无法确定分钟记录属于哪一小时,当然更不能确定该记录属于哪一月,如果要解决这一问题,只有一种方案,即在小时记录中增加月、日两个元素,在分钟记录中增加月、日、时三个元素,通过上述改造,就可以确定链表中的分钟、小时所属的具体时间。改造后的分钟、小时记录数据结构如下:
(8)分钟降水量数据结构
type
TMinPrec=record
Pmin:real;//分钟降水量
Pw,Ps:string;//该分钟内的降水物、整编符
Timerec: Ttzytimerec;//该记录确定分钟所属的具体时间
end;
(9)小时降水量数据结构
小时降水量由该小时内60分钟的降水量记录组成,数据结构定义如下:
type
THourPrec=record
PHour:real;//小时降水量总量
Pw,Ps:string;//该小时内的降水物、整编符
minPrec:Array[0..59] of TminPrec;//小时内60分钟的降水量记录
timerec: Ttzytimerec; //该记录确定小时所属的具体时间
end;
降水资料整编程序涉及的关键数据结构主要是以上几个,下面进行程序设计。
4. 程序设计
数据结构直接决定程序算法的实现方式,根据以上数据结构定义,降水资料整编程序主要有以下5个过程组成:(1)年数据结构的创建;(2)分钟及小时数据记录地址的串联,即两个动态连表的构建;(3)原始时段降水数据分配;(4)月日时降水量统计;(5)连续n分钟最大降水量统计;(6)连续n小时最大降水量统计。
4.1年数据结构的创建
每年12月是固定的,但是月单双之分,所以不同的月份天数不同,因此年数据结构是动态的,其在内存中占用空间是随年月变化的。因此年数据结构的创建需要年、月作为参数。创建过程函数算法描述如下:
Function CreatPYearRec(Year,Mon:integer): TyearPrec;
Var i,j:integer;
YearPrec: TyearPrec;
Begin
For I:=1 to 12 do
Begin
// FunDaysofMonth确定每月实际天数的函数,参数为年、月
YearPrec. MonPrec[i]. Daynum:= FunDaysofMonth (Year,Mon);
//以下对各月的天数据记录动态分配内存
SetLength(YearPrec. MonPrec[i].Dayrec, YearPrec. MonPrec[i]. Daynum);
End;
End;
4.2小时及分钟数据记录指针链表的创建
创建过程函数以年记录做参数,并返回年记录,算法描述如下:
function CreatHouMinLink(Y: TYearPrec):TyearPrec;
begin
Y.PH_hrec:=New(PPhourec); phm:=Y.PH_hrec; //创建小时记录头指针
Y.PH_mrec:=New(PPminrec); pm:=Y.PH_mrec;//创建分钟记录头指针
for I:=1 to 12 do
for J:=1 to Y.Monrec[i].daynum do
begin
for h:=0 to 23 do //对小时记录地址串联
begin
for m:=0 to 59 do //对分钟记录地址串联
begin
Pm^.minrec:=Y.MonRec[i].DayRec[j].HourRec[h].MinRec[m];
Plast:=Pm;
Pn:=New(PPminrec);Pm^.next:=Pn; Pn^.pre:=Pm;Pm:=Pn;
End;
Phm^.hourec:=Y.MonRec[i].DayRec[j].HourRec[h];
Phlast:=Phm;
Phn:=New(PPhourec);Phm^.next:=Phn;
Phn^.pre:=Phm;Phm:=Phn;
end;
end;
Plast:=nil;Phlast:=nil;//尾针地址处理
Result:=Y;
end;
4.3原始时段降水数据分配
即将原始数据分配到年数据结构的每一分钟内。解决方案,首先计算降水时段(SegPRec[n])内每一分钟的降水量Pmin,然后在分钟链表中将降水时段开始时间(SegPRec[n].Begintime)和结束时间(SegPRec[n].Endtime)内的每一分钟数据记录中的降水量置Pmin即可。由于链表中的分钟记录与年数据结构中分钟记录使用同一内存块,这样年数据结构中也有了降水量值。算法如下:
Procdure ProAllocateP;
Begin
For I:=0 to yearPRec.Segnum-1 do//降水时段循环
Begin
Pmin:=ComputePmin(yearPRec. SegPRec[i]);//计算时段的分钟降水量
Proallocatemin(Bt,Et,Pmin, Y.PH_mrec);//对链表中同时段内的降水量赋值
End;
End;
4.4月日时降水量统计
由于年数据结构中没有分钟都有了降水量数值,对月、日、时进行循环计算,即可得到小时、日、月、年的降水量,因为算法非常简单,这里不在进行描述。
4.5 统计连续n分钟的最大降水量统计
由于建立了分钟指针链表,因此算法变的非常简单,描述如下:
function fungetmaxpofnmin(n:integer;ph: PPminrec):real;
begin//n:找连续n分钟的降水量;ph:分钟链表头指针
Pmax:=0;
while PH<>nil do begin
pt:=PH; //从pt开始取连续n个记录之和
Pval:=0;
for i:=1 to n do begin
if Pt<>nil then begin
Pval:=Pval+Pt^.minrec.Pmin;
Pt:=Pt^.next;
end;
end;
If Pval>Pmax then Pmax:=Pval;
PH:=PH^.next;
end;
Result:= Pmax;
end;
连续n小时的最大降水量统计算法与上相同,这里不在描述。
5. 需要说明的问题
5.1内存问题
本程序在运行期间需要较大的内存,内存占用量计算如下:
分钟记录占用内存=每年天数×24小时×60分钟×记录字节数量
=365×24×60×10=5,256,000个字节=5mb
小时记录占用内存=每年天数×24小时×记录字节数量
=365×24×20=175,200个字节=171kb
在加上月记录和原始时段降水量记录,总占用内存约6mb。目前,我国水文资料整编用计算机的内存都应在128mb以上,因此运行该程序存在任何问题。
5.2 数据精度问题
程序对原始数据进行了两次处理,第一次是原始降水量的分配,第二次是分记录的合并,由于目前操作系统基本都是32位的,有的达到了64位,因此数据精度完全可以控制在水文资料整编规范允许范围内。
6. 结语 本设计方案简洁易懂,利于程序实现和维护升级。以上算法已经在北方片水文资料整汇编软件中实现,该算法只是作者个人的观点,可能存在不足之处,希望广大水文同行多提宝贵意见,将算法设计的更好,在此作者表示感谢。 |
|||||||
|
|||||||
| 此功能需激活邮箱后生效。 |
| 程序修改简介(20080418-20080420) < 上一篇 | 下一篇 > 降水量分配算法 |
用户回复


