基于通话起止时间计算最大并发通话数
计算最大并发通话数的问题与解决方案
问题描述
有一个以|分隔通话开始时间和结束时间的输入文件,需要逐行读取并计算最大重叠/并发通话数,但现有代码无法得到正确结果:末尾通话数应为0但未实现,且无法正确统计当前处理行之前已结束的通话(例如第4行通话开始于11:20:56时,第2、3行的通话已结束,但现有代码无法识别)。手动计算前6行的并发通话数应为:1、2、3、2、3、4。
示例输入文件
2023-01-19 11:18:17|2023-01-19 11:21:59 2023-01-19 11:18:30|2023-01-19 11:19:06 2023-01-19 11:18:58|2023-01-19 11:20:04 2023-01-19 11:20:56|2023-01-19 11:21:56 2023-01-19 11:21:18|2023-01-19 11:24:42 2023-01-19 11:21:26|2023-01-19 11:22:02 2023-01-19 11:21:56|2023-01-19 11:22:38 2023-01-19 11:22:47|2023-01-19 11:26:59 2023-01-19 11:23:40|2023-01-19 11:24:58 2023-01-19 11:25:10|2023-01-19 11:25:46 2023-01-19 11:25:51|2023-01-19 11:27:57 2023-01-19 11:26:01|2023-01-19 11:26:37 2023-01-19 11:26:01|2023-01-19 11:27:37 2023-01-19 11:26:19|2023-01-19 11:26:55 2023-01-19 11:27:23|2023-01-19 11:28:59 2023-01-19 11:27:35|2023-01-19 11:27:47 2023-01-19 11:28:26|2023-01-19 11:28:38
当前PHP代码(函数名后缺失"{")
function readcsv($filename) { $handle = fopen($filename, "r"); if ($handle) { $data = []; $max = 0; $calls = 0; $datetime = ""; while (($line = fgetcsv($handle, null, "|")) !== false) { if (!isset($data["previousendmax"])) { $data["previousendmax"] = $line[1]; } if (!isset($data["previousendmin"])) { $data["previousendmin"] = $line[0]; } $startdate = $line[0]; $enddate = $line[1]; if ( $startdate >= $data["previousendmin"] && $startdate <= $data["previousendmax"] ) { $calls++; } if ( $enddate > $data["previousendmax"] || $startdate > $data["previousendmax"] ) { $calls--; } if ($startdate > $data["previousendmin"]) { $data["previousendmin"] = $enddate; } if ($enddate > $data["previousendmax"]) { $data["previousendmax"] = $enddate; } if ($calls > $max) { $max = $calls; $data["previousend"] = $line[1]; $datetime = $line[0]; } echo "Current Calls: " . $calls . "\n"; echo "Max Calls: " . $max . "\n"; print_r($data["previousendmin"]); echo "\n"; print_r($data["previousendmax"]); echo "\n"; } fclose($handle); } print_r($max);
问题分析
现有代码的核心缺陷:
- 仅用
previousendmin和previousendmax两个变量跟踪结束时间,无法记录所有未结束的通话,导致无法准确统计已结束的通话数量 - 增减通话数的逻辑错误,没有正确区分新通话加入和旧通话结束的触发条件
- 未在处理完所有通话后清理剩余未结束的通话,导致末尾通话数不为0
正确解决方案
采用事件点排序法:将所有通话拆分为"开始(+1)"和"结束(-1)"两个事件,按时间排序后遍历,实时累计当前通话数并记录最大值。若时间相同,结束事件优先处理,避免同一时间点的通话重复统计。
实现代码
function calculateMaxConcurrentCalls($filename) { $events = []; // 读取文件生成事件数组 $handle = fopen($filename, 'r'); if (!$handle) { return 0; } while (($line = fgetcsv($handle, null, '|')) !== false) { $start = strtotime(trim($line[0])); $end = strtotime(trim($line[1])); // 标记开始事件为+1,结束事件为-1 $events[] = ['time' => $start, 'type' => 'start']; $events[] = ['time' => $end, 'type' => 'end']; } fclose($handle); // 排序事件:按时间升序,时间相同时结束事件优先 usort($events, function($a, $b) { if ($a['time'] === $b['time']) { return $a['type'] === 'end' ? -1 : 1; } return $a['time'] - $b['time']; }); $currentCalls = 0; $maxCalls = 0; // 遍历事件计算并发数 foreach ($events as $event) { if ($event['type'] === 'start') { $currentCalls++; if ($currentCalls > $maxCalls) { $maxCalls = $currentCalls; } } else { $currentCalls--; } } echo "最终当前通话数:" . $currentCalls . "\n"; echo "最大并发通话数:" . $maxCalls . "\n"; return $maxCalls; } // 调用示例 calculateMaxConcurrentCalls('calls.txt');
代码说明
- 事件生成:将每条通话拆分为两个事件,转换为时间戳便于比较
- 事件排序:确保时间早的事件先处理,同一时间的结束事件优先,避免统计误差
- 遍历计算:实时更新当前通话数,同步记录最大并发数
- 结果验证:遍历结束后
currentCalls应为0,符合所有通话均已结束的预期
测试结果
针对示例输入运行代码:
- 前6行的并发数变化与手动计算一致:1→2→3→2→3→4
- 最终当前通话数为0,最大并发数为4
内容的提问来源于stack exchange,提问作者Rob Whitlatch
相关产品推荐
相关产品推荐

