火车行程分组算法需求:基于最多31天连续时段的桶式分组
火车行程分组算法设计方案
针对你提出的全年火车出行数据分组需求——将含日期、金额(单日多笔)的记录分组到最多覆盖31个连续自然日的桶中,我整理了一套实用的实现思路和方案:
核心需求拆解
首先明确几个关键约束:
- 每个桶的时间范围是连续31个自然日以内(允许不足31天)
- 单日多笔行程记录需全部归入对应日期所在的桶
- 所有行程记录必须被覆盖,且每个记录仅属于一个桶
基础实现思路(无额外优化目标)
如果只是需要完成基础的分组覆盖,不需要平衡桶的金额或其他指标,最直接的方式是按自然日滑动分段,步骤如下:
1. 数据预处理
- 先将所有行程记录按日期升序排序,确保时间顺序正确
- 统一日期格式(推荐
YYYY-MM-DD),避免排序或比较出错 - 可选:构建「日期→对应所有行程记录」的映射表(比如用Python的
defaultdict(list)),后续分组时能快速定位对应日期的记录,提升效率
2. 分段分组逻辑
从最早的行程日期开始,每次取最长的连续31天自然日作为一个桶的时间范围,将该范围内的所有行程记录归入桶中,再从下一个未覆盖的日期重复此操作:
- 确定当前桶的起始日期,计算其往后推30天的日期(即31个连续自然日的结束日期)
- 收集所有日期在「起始日期→结束日期」范围内的行程记录,组成一个桶
- 若剩余未分组的日期不足31天,直接将剩余所有记录归入最后一个桶
伪代码示例(Python)
from datetime import datetime, timedelta from collections import defaultdict def group_train_trips(trips, max_continuous_days=31): # 预处理:按日期排序并构建日期-行程映射 sorted_trips = sorted(trips, key=lambda x: x[0]) if not sorted_trips: return [] date_to_trips = defaultdict(list) for trip in sorted_trips: date_to_trips[trip[0]].append(trip) # 获取所有有行程的日期并排序 all_dates = sorted(date_to_trips.keys()) buckets = [] current_pos = 0 total_dates = len(all_dates) while current_pos < total_dates: # 计算当前桶的时间范围:31个连续自然日 start_date_str = all_dates[current_pos] start_dt = datetime.strptime(start_date_str, "%Y-%m-%d") end_dt = start_dt + timedelta(days=max_continuous_days - 1) end_date_str = end_dt.strftime("%Y-%m-%d") # 找到当前桶覆盖的最后一个有行程的日期索引 end_pos = current_pos while end_pos < total_dates and all_dates[end_pos] <= end_date_str: end_pos += 1 # 收集当前桶的所有行程记录 bucket_records = [] for date in all_dates[current_pos:end_pos]: bucket_records.extend(date_to_trips[date]) # 计算桶的统计信息(可选) total_amount = sum(trip[1] for trip in bucket_records) buckets.append({ "date_range": f"{start_date_str} ~ {all_dates[end_pos-1]}", "actual_days_with_trips": end_pos - current_pos, "total_trips": len(bucket_records), "total_amount": round(total_amount, 2), "trips": bucket_records }) current_pos = end_pos return buckets # 测试用例 sample_trips = [ ("2024-01-01", 50), ("2024-01-01", 30), ("2024-01-15", 45), ("2024-02-02", 60), ("2024-02-28", 70) ] print(group_train_trips(sample_trips))
进阶优化:平衡桶的金额分布
如果你的需求不仅是分组,还希望每个桶的总金额尽量均衡(比如避免某个桶金额过高),可以采用动态规划的思路:
- 定义
dp[i]为前i个有行程的日期分组后的最优代价(比如总金额的方差、最大桶金额等) - 转移方程:
dp[i] = min(dp[j] + cost(j+1, i)),其中j的取值范围是max(0, i-31)到i-1,cost(j+1,i)表示将第j+1到i个日期的行程归入一个桶的代价 - 通过遍历所有可能的分段点,找到代价最小的分组方式
注意事项
- 日期处理要注意跨月、跨年的情况,使用标准日期库(如Python的
datetime)避免手动计算出错 - 若数据量极大(百万级以上),可以考虑用批量处理或数据库查询(比如SQL的窗口函数)来提升效率,避免内存瓶颈
内容的提问来源于stack exchange,提问作者Curtis




