改进版计费时间计算法
最编程
2024-08-06 18:16:51
...
1,问题的提出
停车场管理中,有些月卡是按时段免费停车的,也就是在指定的时间区间内停车,免收停车费;而在此区间以外的时间,则累计为计费时长,需要缴费。
该问题的输入是车辆进出时间T1、T2、免费时段起止时间点sh,eh
输出是计费时长,分钟为单位。
2,问题分析
咋看该问题并不复杂,采用时间点比较法,无非是在T1、T2之间找到免费区间,然后予以扣除即可。
但事实上,需要考虑的因素有:
免费时段起止时间点sh,eh,可能在一天内,也可能分布在2天内,也就是要考虑跨天的因素。
T1到T2的时间跨度X,与sh、eh构造的时间段Y之间的关系较为复杂。
假如考虑sh、eh跨天,那么2天之内,免费的区段就有两个,记为Y1、Y2,其中 Y2在Y1后面
X和Y1、Y2的关系就有如下可能性:
- X在Y1左侧
- X在Y2右侧
- X包含Y1和Y2
- X与Y1交叉但不包含Y1
- 。。。
粗略估计,位置关系有10种以上
若再考虑比较时,跨天的时段还得以0点为界,分解为两段,那么算法将非常复杂,极易因考虑不周出错。
3,新的算法思路
由于采用时间点(一天之内的时刻数)比对统计非常复杂,新的思路是采用日期排序比对法。
把所有时间点换算成日期,然后比对,在免费时段就不统计,在非免费时段就加入统计。此法简单明了。
以下是详细步骤:
- 取出T1、T2时间对应的日期D1/D2
- (技巧)将D1减去一天,设置为计费起始日的前一天。目的是,判断计费起始时间是否处于免费时段。
- 将D1和D2之间的日期,连续插入,形成日期列表DL:【D1、C11、C12、…、D2】
- 将DL与sh和eh组合,形成两个时间列表
-
- TLs:【D1s、C11s、C12s、…、D2s】
- TLe:【D1e、C11e、C12e、…、D2e】
5. 将T1/T2/TLs/TLe,组合为一个列表CL:【t1,t2,…,tn】
-
- l 按时间排序
- l TLs的元素,附加s标记
- l TLe的元素,附加e标记
6. 初始化参数:
-
- 计费时长Tc=0,起始时点Ts=0,计费标记Tf=0
7. 对CL中的元素,顺序执行以下算法:
-
- 取出元素,ti
- 若ti>T1,则Tc +=(ti-Ts)*Tf,累计收费时间
- 若ti=T2,则 退出。
- 若ti=T1,则 Ts=ti
- 若ti的标记为s,则 Ts=ti,Tf=0,进入免费区间
- 若ti的标记为e,则 Ts=ti,Tf=1,进入收费区间
4,参考代码
以下delphi代码,实现了上述思路,算法简明,不易出错,对于跨天、跨月、跨年等均不存在问题,经测试计算结果OK。
function TForm1.CalcMCFeeMinutesV2(sh, eh:integer; sDate, eDate: PChar):integer; var d1, d2: TDatetime; Tc: double; Ts: TDatetime; Tf: integer; i: integer; Date1, Date2, curTime: TDatetime; DL, TL: TStringList; s_sh, s_eh: string; begin memo1.Lines.Clear ; memo1.Lines.Add('出入时间:' + sDate + '--' + eDate); d1 := StrTodateTime(sDate); d2 := StrTodateTime(eDate); //月卡计费无免费时段,直接返回所有分钟数 if sh=eh then begin Result := round((d2 -d1)*24*60); Exit; end; Date1 := StrToDatetime(formatDatetime('YYYY-MM-DD', d1)); //取日期整数 Date2 := StrToDatetime(formatDatetime('YYYY-MM-DD', d2)); //取日期整数 //格式化时间,以便后面排序 s_sh := IntToStr(sh); if length(s_sh)=1 then s_sh := '0'+ s_sh; s_eh := IntToStr(eh); if length(s_eh)=1 then s_eh := '0'+ s_eh; DL := TStringList.Create; TL := TStringList.Create; //要多出一天来,以便判断起始时间处于收费时段还是免费时段 Date1 := Date1 -2; for i:=0 to (trunc (d2 -d1)+1) do begin Date1 := Date1 + 1; if Date1>date2 then break; DL.Add(formatDatetime('YYYY-MM-DD', Date1)); end; for i:=0 to DL.Count-1 do begin TL.AddObject(DL[i]+' '+ s_sh +':00' , Pointer(1)); TL.AddObject(DL[i]+' '+ s_eh +':00' , Pointer(2)); end; TL.AddObject(formatDatetime('YYYY-MM-DD HH:MM', d1) , Pointer(3)); TL.AddObject(formatDatetime('YYYY-MM-DD HH:MM', d2) , Pointer(4)); TL.Sort; for i:=0 to TL.Count-1 do begin memo1.Lines.Add(TL[i] +'/'+ IntToStr(Integer(TL.Objects[i]))); end; //初始化参数:计费时长Tc=0,起始时点Ts=0,计费标记Tf=0 Tc := 0; Ts := StrToDatetime(TL[0]); Tf := 1; for i:=0 to TL.Count-1 do begin curTime := StrToDatetime(TL[i]); if Integer(TL.Objects[i])=4 then begin Tc := Tc + (curTime- Ts)* Tf; memo1.Lines.Add(TL[i]+ format('/累计:%f',[Tc])); Result := Round(Tc*24*60); WriteMemoToLogFile('', memo1.Text ); Exit; end; if Integer(TL.Objects[i])=3 then begin //if curTime>d1 then Tc := Tc + (curTime- Ts)* Tf; Ts := curTime; end; if Integer(TL.Objects[i])=1 then begin if curTime>d1 then Tc := Tc + (curTime- Ts)* Tf; Ts := curTime; Tf := 0; end; if Integer(TL.Objects[i])=2 then begin if curTime>d1 then Tc := Tc + (curTime- Ts)* Tf; Ts := curTime; Tf := 1; end; memo1.Lines.Add(TL[i]+ format('/累计:%f',[Tc])); end; end;
推荐阅读
-
改进版计费时间计算法
-
改进版的二分查找加速插入排序算法
-
学习改进版组合导航算法:运用因子图优化的方法
-
改进版:利用MATLAB中的粒子群算法提升BP神经网络对气温的预测——附带Matlab源代码(第1302期)
-
改进版:利用MATLAB粒子群算法调优多输入多输出的BP神经网络【附带Matlab源代码,第1418期刊】
-
用Matlab实现的改进版粒子群算法(第三部分)
-
改进版 Tent 混沌映射的粒子群优化算法 - 配套Matlab源代码,第940期刊载
-
C++ SUNDY 算法(BM 算法的改进版)
-
排序算法:气泡排序(改进版)的思路分析和代码实现
-
[排序算法] 气泡排序(改进版)的思路分析和代码实现细节 - 二次行程排序: