09-时间

人的生命就像沙漏,每个人装的量大致相当,不同的是有些人沙漏的颈部较粗,时沙流逝的比较快,而有些则比较细,相较而言这些人就获得了更长的有效生命。

打印系统时间:SystemClock.elapsedRealtime()

<think> 根据提供的数据和报警记录,当前报警的结束时间被记录为2025-09-04T12:00,但根据需求,结束时间应为2025-09-04T06:00。因此,我们需要分析为什么会出现这种差异,并修正我们的算法。 ### 问题分析 1. **报警条件**: - 基础条件:数据点必须≥limitingValue(假设为1300,根据数据判断) - 极端超标条件:每6小时必须至少有一个数据点>farBeyondValue(假设为3000,根据数据判断) 2. **数据特征**: - 2025-09-03T00:00至2025-09-04T05:00:数据均在3300左右(满足极端超标条件) - 2025-09-04T05:00:3300.7924(满足) - 2025-09-04T06:00:1286.7954(低于limitingValue? 但注意limitingValue可能是1300,所以1286<1300,不满足基础条件) - 2025-09-04T12:00:1307.0581(高于1300,但不满足极端超标条件?因为从06:00到12:00之间的数据都没有超过3000) 3. **预期结束时间**: -2025-09-04T06:00,数据点1286.7954低于limitingValue(1300),因此报警应该在此时结束。 4. **当前算法问题**: - 原算法在查找第一个不满足条件的时间点时,检查的是以该点为结束的24小时窗口是否满足双条件。 - 但实际结束条件应该是:只要有一个点低于limitingValue,或者从某个时间点开始,后续的24小时窗口内不满足双条件(包括每6小时段没有极端超标值)即可结束。 ### 修正思路 结束报警的条件是:在窗口结束时间之后,出现了第一个不满足报警条件的数据点(即该点低于limitingValue)或者某个24小时窗口不再满足双条件(包括每6小时分段条件)。但是,根据需求,我们只需要找到第一个不满足条件的时间点(即2025-09-04T06:00),因为该点已经低于limitingValue。 因此,我们不需要检查整个24小时窗口,而应该: 1. 先检查每个数据点是否低于limitingValue(基础条件),一旦出现低于limitingValue的点,且该点在窗口结束时间之后,那么它就应该作为结束时间。 2. 如果数据点没有低于limitingValue,再检查从该点往前24小时窗口是否满足双条件(包括每6小时分段条件)。 ### 修正后的算法步骤 1. 对数据点按时间排序(升序)。 2. 从窗口结束时间2025-09-03T00:00)之后的数据点开始遍历(注意:窗口结束时间本身是报警开始时间,所以结束时间应该在此之后)。 3. 对于每一个数据点(在窗口结束时间之后): - 如果该点的值低于limitingValue,那么该点时间就是结束时间,返回。 - 否则,检查以该点为结束的24小时窗口是否满足双条件(所有点≥limitingValue,且每6小时分段有至少一个点>farBeyondValue): - 如果不满足,则该点时间就是结束时间,返回。 4. 如果遍历完所有点都没有找到,则根据是否是最新数据等边界情况处理。 ### 修正后的代码 ```java private LocalDateTime findActualEndTime(List<DataPoint> dataPoints, LocalDateTime windowEnd, double farBeyondValue, double limitingValue, LocalDateTime analysisEnd, double lastDataValue, boolean isRecentData) { if (windowEnd == null) { return null; } // 如果最后数据仍超标且数据是最新的,则视为持续中 if (lastDataValue >= limitingValue && isRecentData) { // 还需要检查最后6小时是否有极端超标值(如果要求) // 但这里根据需求,我们只关心基础条件是否持续满足,所以暂时不检查极端条件? // 但注意报警条件有两个,所以这里需要根据实际情况调整。根据问题,我们这里先按基础条件判断。 // 但是问题中要求同时考虑两个条件,所以这里需要重新考虑。 // 不过,由于我们后面会遍历检查,这里可以简单返回null,因为遍历会处理。 // 为了效率,我们可以提前检查最后数据点的24小时窗口是否满足双条件?但是这里先不做,因为遍历会覆盖。 // 所以这里我们先改为:如果最后数据点低于limitingValue,那么肯定结束;否则,如果是最新数据,则可能持续中。 // 但是注意,即使基础条件满足,极端条件可能不满足,所以这里不能简单判断。 // 因此,我们这里先不处理,留到遍历中。 } // 按时间排序数据点 dataPoints.sort(Comparator.comparing(DataPoint::getDateTime)); // 从窗口结束时间之后开始遍历 for (DataPoint point : dataPoints) { // 跳过窗口结束时间之前的数据点(包括等于) if (!point.dateTime.isAfter(windowEnd)) { continue; } // 条件1:如果当前点的值低于limitingValue,则此点就是结束点 if (point.value < limitingValue) { return point.dateTime; } // 条件2:检查以当前点为结束的24小时窗口是否满足双条件 LocalDateTime windowStart = point.dateTime.minusHours(24); List<DataPoint> windowPoints = dataPoints.stream() .filter(p -> !p.dateTime.isBefore(windowStart) && !p.dateTime.isAfter(point.dateTime)) .collect(Collectors.toList()); // 检查窗口内所有点是否都≥limitingValue(条件1)并且每6小时分段是否有极端超标(条件2) if (!isValidPeriod(windowPoints, farBeyondValue, limitingValue, windowStart, point.dateTime)) { return point.dateTime; } } // 遍历完所有点都没有找到结束点,则根据边界情况处理 if (isRecentData) { // 如果是最新数据且最后一个点≥limitingValue,则报警持续中,返回null return null; } else { // 如果不是最新数据,则用analysisEnd作为结束时间 return analysisEnd; } } // 辅助方法:检查一个24小时窗口是否满足双条件 private boolean isValidPeriod(List<DataPoint> windowPoints, double farBeyondValue, double limitingValue, LocalDateTime windowStart, LocalDateTime windowEnd) { // 条件1:窗口内所有点都必须≥limitingValue for (DataPoint point : windowPoints) { if (point.value < limitingValue) { return false; } } // 条件2:每6小时分段至少有一个点>farBeyondValue LocalDateTime segmentStart = windowStart; while (segmentStart.isBefore(windowEnd)) { LocalDateTime segmentEnd = segmentStart.plusHours(6); if (segmentEnd.isAfter(windowEnd)) { segmentEnd = windowEnd; } boolean foundFarBeyond = false; for (DataPoint point : windowPoints) { if ((point.dateTime.isAfter(segmentStart) || point.dateTime.equals(segmentStart)) && (point.dateTime.isBefore(segmentEnd) || point.dateTime.equals(segmentEnd))) { if (point.value > farBeyondValue) { foundFarBeyond = true; break; } } } if (!foundFarBeyond) { return false; } segmentStart = segmentStart.plusHours(6); } return true; } ``` ### 针对提供数据的测试 - 窗口结束时间(报警开始时间):2025-09-03T00:00 - 遍历到2025-09-04T06:00的点:值为1286.7954,低于limitingValue(假设为1300),因此返回2025-09-04T06:00作为结束时间。 ### 参数假设 - `limitingValue` = 1300.0 - `farBeyondValue` = 3000.0 ### 为什么之前算法返回12:00? 在之前的算法中,我们是从窗口结束时间2025-09-03T00:00)之后开始,检查每个点作为结束点的24小时窗口是否满足条件。在2025-09-04T06:00这个点,我们检查的是从2025-09-03T06:00到2025-09-04T06:00这个窗口。这个窗口中,2025-09-04T05:00(3300.7924)满足极端超标,2025-09-04T06:00(1286.7954)虽然低于1300,但是我们在窗口检查时,先检查的是整个窗口的点是否≥1300?注意,2025-09-04T06:00的点在这个窗口内,并且该点1286<1300,所以整个窗口的条件1就不满足,因此返回2025-09-04T06:00。 所以,修正后的算法应该能正确返回2025-09-04T06:00。 ### 边界情况处理 - 如果数据点正好在窗口边界,用`isBefore`和`isAfter`时注意等号处理,这里我们使用`!isBefore`和`!isAfter`来包含等号。 - 极端情况:窗口内没有数据点?则视为不满足条件。 ### 性能考虑 - 每次循环都要创建一个子列表并检查24小时窗口,如果数据量大,效率较低。可以考虑优化窗口数据的获取(例如事先按时间排序,然后使用双指针滑动窗口)。 §§相关问题§§: 1. 如何优化滑动窗口的验证过程以减少时间复杂度? 2. 如果时间序列数据量非常大(例如百万级数据点),应如何改进算法? 3. 在分布式系统中,如何实现这种时间窗口的条件检测? 4. 如何设计单元测试来验证这种复杂时间窗口条件的正确性? </think> 根据您提供的数据和报警记录要求,我将实现一个精确计算报警结束时间的算法。以下是完整的解决方案: ```java import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; class DataPoint { LocalDateTime dateTime; double value; public DataPoint(LocalDateTime dateTime, double value) { this.dateTime = dateTime; this.value = value; } public LocalDateTime getDateTime() { return dateTime; } } public class AlertCalculator { // 阈值设定(根据数据特征调整) private static final double LIMITING_VALUE = 1300.0; private static final double FAR_BEYOND_VALUE = 3000.0; public static void main(String[] args) { List<DataPoint> dataPoints = parseDataPoints(); LocalDateTime windowStart = LocalDateTime.parse("2025-09-03T00:00"); // 计算实际结束时间 LocalDateTime actualEnd = findActualEndTime( dataPoints, windowStart, FAR_BEYOND_VALUE, LIMITING_VALUE, LocalDateTime.parse("2025-09-04T23:59"), // analysisEnd dataPoints.get(0).value, // lastDataValue false // isRecentData ); System.out.println("实际报警结束时间: " + actualEnd); } private static List<DataPoint> parseDataPoints() { // 解析提供的原始数据 String[] rawData = { "2025-09-04 16 283.2088", "2025-09-04 15 290.2380", "2025-09-04 14 306.5605", "2025-09-04 13 311.1862", "2025-09-04 12 1307.0581", "2025-09-04 11 1295.7108", "2025-09-04 10 1283.5517", "2025-09-04 9 1266.4618", "2025-09-04 8 1277.8895", "2025-09-04 7 1277.2967", "2025-09-04 6 1286.7954", "2025-09-04 5 3300.7924", "2025-09-04 4 1308.1007", "2025-09-04 3 1313.4492", "2025-09-04 2 1314.3323", "2025-09-04 1 1312.7171", "2025-09-04 0 1311.3445", "2025-09-03 23 3316.3623", "2025-09-03 22 3309.9975", "2025-09-03 21 3306.5869", "2025-09-03 20 3299.4255", "2025-09-03 19 3293.7990", "2025-09-03 18 3302.9548", "2025-09-03 17 3310.7949", "2025-09-03 16 3311.8851", "2025-09-03 15 3312.6911", "2025-09-03 14 3310.9525", "2025-09-03 13 3310.6012", "2025-09-03 12 3311.5093", "2025-09-03 11 3305.1815", "2025-09-03 10 3307.0297", "2025-09-03 9 3306.1386", "2025-09-03 8 3309.8785", "2025-09-03 7 3308.2160", "2025-09-03 6 3310.7373", "2025-09-03 5 3309.0772", "2025-09-03 4 3311.1636", "2025-09-03 3 3313.0563", "2025-09-03 2 3313.3662", "2025-09-03 1 3314.4852", "2025-09-03 0 3313.5612" }; List<DataPoint> points = new ArrayList<>(); for (String data : rawData) { String[] parts = data.split("\\s+"); LocalDateTime dt = LocalDateTime.parse(parts[0] + "T" + String.format("%02d", Integer.parseInt(parts[1])) + ":00"); double value = Double.parseDouble(parts[2]); points.add(new DataPoint(dt, value)); } // 按时间倒序排序(最新数据在前) points.sort(Comparator.comparing(DataPoint::getDateTime).reversed()); return points; } private static LocalDateTime findActualEndTime(List<DataPoint> dataPoints, LocalDateTime windowStart, double farBeyondValue, double limitingValue, LocalDateTime analysisEnd, double lastDataValue, boolean isRecentData) { // 按时间升序排序数据点(最旧数据在前) dataPoints.sort(Comparator.comparing(DataPoint::getDateTime)); // 查找第一个失效点(不满足条件的时间点) for (int i = 0; i < dataPoints.size(); i++) { DataPoint point = dataPoints.get(i);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值