基于Barn轮换复用的农场需求计算(TypeScript实现问题)
问题描述
我有一份包含200-300条记录的XLSX文件,记录格式如下:
| Date | Barns |
|---|---|
| 14/May/2024 | 13 |
| 15/May/2024 | 14 |
| 16/May/2024 | 16 |
每条记录代表孵化场每日产出鸡群对应的Barn数量(每座Barn容纳33000只鸡)。已将数据转换为JSON并存入数据库,现需计算容纳这些鸡群所需的农场数量。
规则说明:
- 每个农场可容纳16座Barn;
- 每座Barn饲养鸡群33天后售出,售出后需空置22天才能再次使用(即56天后可复用)。
当前TypeScript代码未考虑Barn 56天后可复用的规则,仅在农场填满时持续新增农场,无法准确计算所需农场数量,现有代码如下:
const settings = { BARNS_PER_FARM: 16 } const totalBarns = planDays.reduce((acc, day) => acc + day.barns, 0) const firstDate = planDays[0]?.date const lastDate = planDays[planDays.length - 1]?.date const farmsNeeded = Math.ceil(totalBarns / settings.BARNS_PER_FARM) const copy = [...planDays] const farms: Farm[] = Array.from({ length: farmsNeeded }).map((_, i) => { return { name: `Farm ${i + 1}`, housing: [], lastUsedDate: null, // Track last used date for reusability } }) const current = { farm: 0, } for (const day of copy) { const barns = day.barns if (barns === 0) { continue } const farm = farms[current.farm] const available = settings.BARNS_PER_FARM - farmUsedBarns(farm) const needed = barns if (available >= needed) { addHousing(farm, needed, day.date) } else { if (available > 0) { addHousing(farm, available, day.date) } let remaining = needed - available while (remaining > 0) { current.farm++ const farm = farms[current.farm] const available = settings.BARNS_PER_FARM - farmUsedBarns(farms[current.farm]) const needed = remaining if (available >= needed) { addHousing(farm, needed, day.date) remaining = 0 } else { addHousing(farm, available, day.date) remaining -= available } } } } return { status: 200, body: { data: { farms: farms.filter(farm => farm.housing.length > 0), totalBarns, firstDate, lastDate, farmsNeeded, }, }, } function addHousing(farm: Farm, numberOfBarns: number, startDate: Date) { if (farm.housing.length === 0) { farm.housing.push({ name: 'A', startDate: startDate, numberOfBarns, }) return } const previousHousing = farm.housing[farm.housing.length - 1] const newStartDate = new Date(previousHousing.startDate) newStartDate.setDate(newStartDate.getDate() + 1) farm.housing.push({ name: String.fromCharCode(previousHousing.name.charCodeAt(0) + 1), numberOfBarns, startDate: startDate, }) } function farmUsedBarns(farm: Farm) { return farm.housing.reduce((acc, housing) => { return acc + housing.numberOfBarns }, 0) }
优化方案
核心思路
- 跟踪Barn可用时间:给每个农场的批次记录添加
endDate(即该批次Barn可再次使用的日期,startDate + 56天),用于判断当前日期是否有可用Barn; - 优先复用现有容量:每日分配Barn时,先遍历所有已创建的农场,统计可用Barn数量,优先使用这些容量;
- 动态创建农场:当现有农场可用容量总和无法满足当日需求时,再新建农场补充剩余需求;
- 精准统计农场数:不再提前预估农场数量,而是根据实际分配结果统计最终使用的农场总数。
优化后的完整代码
// 定义类型确保类型安全 type HousingRecord = { name: string; startDate: Date; endDate: Date; // 新增:Barn可复用的日期 numberOfBarns: number; }; type Farm = { name: string; housing: HousingRecord[]; }; const settings = { BARNS_PER_FARM: 16, BARN_REUSE_DAYS: 56, // 33天饲养 + 22天空置 }; export function calculateRequiredFarms(planDays: { date: Date; barns: number }[]) { const totalBarns = planDays.reduce((acc, day) => acc + day.barns, 0); const firstDate = planDays[0]?.date; const lastDate = planDays[planDays.length - 1]?.date; const farms: Farm[] = []; // 计算单个农场在指定日期的可用Barn数量 const getAvailableBarnsForFarm = (farm: Farm, currentDate: Date): number => { const usedBarns = farm.housing.reduce((acc, housing) => { // 当前日期早于endDate时,Barn仍处于占用状态 return currentDate < housing.endDate ? acc + housing.numberOfBarns : acc; }, 0); return settings.BARNS_PER_FARM - usedBarns; }; // 为农场添加批次记录,自动生成名称和endDate const addHousingBatch = (farm: Farm, numberOfBarns: number, startDate: Date) => { const nextName = farm.housing.length === 0 ? 'A' : String.fromCharCode(farm.housing[farm.housing.length - 1].name.charCodeAt(0) + 1); const endDate = new Date(startDate); endDate.setDate(endDate.getDate() + settings.BARN_REUSE_DAYS); farm.housing.push({ name: nextName, startDate, endDate, numberOfBarns, }); }; for (const day of planDays) { let remainingBarns = day.barns; if (remainingBarns <= 0) continue; // 1. 优先使用已有农场的可用Barn for (const farm of farms) { if (remainingBarns <= 0) break; const available = getAvailableBarnsForFarm(farm, day.date); if (available <= 0) continue; const useCount = Math.min(available, remainingBarns); addHousingBatch(farm, useCount, day.date); remainingBarns -= useCount; } // 2. 剩余需求新建农场 while (remainingBarns > 0) { const newFarm: Farm = { name: `Farm ${farms.length + 1}`, housing: [], }; farms.push(newFarm); const available = settings.BARNS_PER_FARM; const useCount = Math.min(available, remainingBarns); addHousingBatch(newFarm, useCount, day.date); remainingBarns -= useCount; } } // 过滤未使用的农场 const usedFarms = farms.filter(farm => farm.housing.length > 0); return { status: 200, body: { data: { farms: usedFarms, totalBarns, firstDate, lastDate, farmsNeeded: usedFarms.length, // 实际所需农场数 }, }, }; }
关键改进点
- 新增
endDate字段精准跟踪Barn复用时间,替代原有的模糊lastUsedDate; getAvailableBarnsForFarm函数实时计算农场可用容量,避免累计统计的误差;- 分配逻辑改为先复用再新建,大幅减少不必要的农场数量;
- 动态计算实际所需农场数,结果更贴合业务规则。
内容的提问来源于stack exchange,提问作者Mohammed
相关产品推荐
相关产品推荐

