我有收集时间戳,例如10:18:07.490,11:50:18.251
,其中第一个是开始时间,第二个是事件的结束时间。我需要找到一个范围,其中最大事件发生在24小时之内。这些事件的精确度为毫秒。
我所做的是以毫秒为单位划分24小时,并在每毫秒附加事件,然后找到发生最大事件的范围。
LocalTime start = LocalTime.parse("00:00");
LocalTime end = LocalTime.parse("23:59");
for (LocalTime x = start; x.isBefore(end); x = x.plus(Duration.ofMillis(1))) {
for (int i = 0; i < startTime.size(); i++) {
if (startTime.get(i).isAfter(x) && endTime.get(i).isBefore(x))
// add them to list;
}
}
当然这不是一个好方法,它需要太多的记忆。我怎么能以适当的方式做到这一点?有什么建议吗?
答案 0 :(得分:5)
如果您愿意使用第三方库,可以使用jOOλ的窗口函数在SQL样式中“相对容易”实现。这个想法与amit's answer中解释的相同:
System.out.println(
Seq.of(tuple(LocalTime.parse("10:18:07.490"), LocalTime.parse("11:50:18.251")),
tuple(LocalTime.parse("09:37:03.100"), LocalTime.parse("16:57:13.938")),
tuple(LocalTime.parse("08:15:11.201"), LocalTime.parse("10:33:17.019")),
tuple(LocalTime.parse("10:37:03.100"), LocalTime.parse("11:00:15.123")),
tuple(LocalTime.parse("11:20:55.037"), LocalTime.parse("14:37:25.188")),
tuple(LocalTime.parse("12:15:00.000"), LocalTime.parse("14:13:11.456")))
.flatMap(t -> Seq.of(tuple(t.v1, 1), tuple(t.v2, -1)))
.sorted(Comparator.comparing(t -> t.v1))
.window(Long.MIN_VALUE, 0)
.map(w -> tuple(
w.value().v1,
w.lead().map(t -> t.v1).orElse(null),
w.sum(t -> t.v2).orElse(0)))
.maxBy(t -> t.v3)
);
以上版画:
Optional[(10:18:07.490, 10:33:17.019, 3)]
所以,在10:18 ......和10:33之间......期间,有3个事件,这是在白天任何时候重叠的事件数量最多。
请注意,样本数据中有3个并发事件有几个时期。 maxBy()
仅返回第一个此类期间。为了返回所有这些句点,请使用maxAllBy()
代替(添加到jOOλ0.9.11):
.maxAllBy(t -> t.v3)
.toList()
屈服然后:
[(10:18:07.490, 10:33:17.019, 3),
(10:37:03.100, 11:00:15.123, 3),
(11:20:55.037, 11:50:18.251, 3),
(12:15 , 14:13:11.456, 3)]
3 /-----\ /-----\ /-----\ /-----\
2 /-----/ \-----/ \-----/ \-----/ \-----\
1 -----/ \-----\
0 \--
08:15 09:37 10:18 10:33 10:37 11:00 11:20 11:50 12:15 14:13 14:37 16:57
以下是原始解决方案的评论:
// This is your input data
Seq.of(tuple(LocalTime.parse("10:18:07.490"), LocalTime.parse("11:50:18.251")),
tuple(LocalTime.parse("09:37:03.100"), LocalTime.parse("16:57:13.938")),
tuple(LocalTime.parse("08:15:11.201"), LocalTime.parse("10:33:17.019")),
tuple(LocalTime.parse("10:37:03.100"), LocalTime.parse("11:00:15.123")),
tuple(LocalTime.parse("11:20:55.037"), LocalTime.parse("14:37:25.188")),
tuple(LocalTime.parse("12:15:00.000"), LocalTime.parse("14:13:11.456")))
// Flatten "start" and "end" times into a single sequence, with start times being
// accompanied by a "+1" event, and end times by a "-1" event, which can then be summed
.flatMap(t -> Seq.of(tuple(t.v1, 1), tuple(t.v2, -1)))
// Sort the "start" and "end" times according to the time
.sorted(Comparator.comparing(t -> t.v1))
// Create a "window" between the first time and the current time in the sequence
.window(Long.MIN_VALUE, 0)
// Map each time value to a tuple containing
// (1) the time value itself
// (2) the subsequent time value (lead)
// (3) the "running total" of the +1 / -1 values
.map(w -> tuple(
w.value().v1,
w.lead().map(t -> t.v1).orElse(null),
w.sum(t -> t.v2).orElse(0)))
// Now, find the tuple that has the maximum "running total" value
.maxBy(t -> t.v3)
我已经写了更多关于window functions and how to implement them in Java in this blog post的信息。
(免责声明:我为jOOλ背后的公司工作)
答案 1 :(得分:3)
在内存方面可以做得更好(假设O(n)被认为对你有好处,并且你不认为24 * 60 * 60 * 1000是可容忍的常数):
[time, type]
(时间是时间,类型是
无论是开始还是结束)。通过存储“到目前为止看到的最大值”,您可以轻松识别出现最大事件数的单个点。
如果你想获得包含这一点的间隔,你可以简单地找到“第一个最大值”发生的时间,直到它结束的时间(这是下一个[time, type]
对,或者你是否允许开始,结束为了在一起而不计算,只是从这一点开始线性扫描,直到计数器减少并且时间移动,这只能进行一次,并且不会改变算法的总复杂度)。
这很容易修改这种方法来从点