时间段合并,有限数值去重

      几天前遇到这么一个问题:有一个文件记录大量用户的上网记录,要求合并这些信息统计出每个用户的上网时间段。文件中的记录如下:

用户1 开始时间9:15结束时间9:18

用户3 开始时间8:22结束时间8:45

用户1 开始时间9:10结束时间9:16

用户2 开始时间8:31结束时间8:52

用户1 开始时间10:23结束时间10:47

用户1 开始时间9:22结束时间9:38

.......

计算结果是:

用户1 (9:10-18  9:22-9:38  10:23-10:47)

用户2 (8:31-8:52)

用户3 (8:22-8:45)

文件中的记录数据量很大几个亿,用户数量和每个用户的记录都很多,时间是只包含一天中的时间,或许还会要求精确到秒级别,我们以分钟为单位讨论。

 

解决方案:

      按照用户将此问题分解为合并一个用户的上网时间段,每个时间段转化成当天的分钟数(如果精确到秒则转化成当天的秒数)如14:45 是14*60+45=885分钟;或者15:32:21是(15*60+32)*60+21=55941秒。

      这样就将问题转化成合并数值段问题最数值为1440-1(精确到分钟)或者86400-1(精确到秒)毫秒类似。这样把问题转化类似成在一个数值范围内的有限数值去重的问题。可以很快想到很多解决方案,用集合,数组,排序等等。

二进制存储结构保存结果:

       Java中一个int型数值是32位,我们用一个int数值可以保存32个结果,如时间段是5-27我们将int数值从低5位到27为置1,即使0x07 FF FF F0(0000 0111 1111 1111 1111 1111 1111 0000),因为我们最大值是(86400-1)一个int数值明显不够,可以采用int数组a = int[2700]来保存(一个int可以保存32个结果,86400个只需要2700个int即可)。a[0]记录0-31的结果a[1]记录32-63的结果...依次类推。

      数值初始化为0,每处理一个数值段的时候(8776-9844)只需要将数组中对应的位置置1即可如下面的putNum函数,处理完所有数据后,再将结果转化成一个一个时间段就是我们需要的结果如下面的print函数。

int[] a = new int[10000];

public static void putNum(int start,int stop){

int atStart = start>>5;

int begin = start<<27>>>27;

int atStop = stop>>5;

int end = stop<<27>>>27;

for(int i = atStart+1; i < atStop; i++){

a[i] = 0xFFFFFFFF;

}

if(atStart == atStop) {

a[atStart] = a[atStart] | (0xFFFFFFFF << begin & 0xFFFFFFFF >>> 32-end);

} else {

a[atStart] = a[atStart] | (0xFFFFFFFF << begin);

a[atStop] = a[atStop] | (0xFFFFFFFF >>>31-end);

}

}

 

public static void print(){

boolean currStateIsStart = false;

for(int i = 0; i <a.length; i++){

int num = 1;

for(int j = 0; j < 32; j++){

if((a[i] & num) != 0){

if(!currStateIsStart){

currStateIsStart = true;

System.out.print("开始:" + ((i << 5) + j));

}

} else {

if(currStateIsStart){

currStateIsStart = false;

System.out.println("结束:" + ((i << 5) + j - 1));

}

}

num = num<<1;

}

}

if(currStateIsStart){

System.out.println("最后结束:" + (a.length << 5 - 1));

}

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值