对于Ruby on Rails规划应用程序,我遇到了一个我无法有效解决的算法/组合问题。
在我的应用程序中,我有两种类型的记录:
两种类型的记录都是由开始和结束时间定义的,而且可用性还有其他类型(可用,待机,不可用)。
现在,我希望得到一个非重叠期间的详细列表,这些期间会显示某人当前有计划记录,但另外还有可用性
举个例子:
Time: 0-----------6-----------12-----------18-----------24
Avail: |-----available-----||--standby--|
Plans: |------------------|
Result: |------||------------------||----|
期望的结果是3个非重叠期:
另一个需要拆分可用性的示例:
Time: 0-----------6-----------12-----------18-----------24
Avail: |-----available-----||--standby--|
Plans: |-----|
Result: |------||-----||----||-----------|
期望的结果是3个非重叠期:
我已经在数组中拥有所有(重叠)句点。什么是有效实现我想要的最佳方式?
答案 0 :(得分:0)
我认为我们会给出计划','可用的小时范围'和'覆盖',其中'覆盖'是'可用`范围之前和之后是'备用'范围。此外,'覆盖'包含'计划'以及其中的任何一个或两个“待命”范围可能为零持续时间。
<强>代码强>
def categories(avail, plan)
adj_avail = adj_avail(avail)
adj_plan = adj_plan(avail, plan)
arr = []
finish = [adj_avail[:available][:start], adj_plan[:start]].min
add_block(arr, :standby, adj_avail[:coverage][:start], finish)
start = finish
finish = [adj_avail[:available][:finish], adj_plan[:start]].min
add_block(arr, :available, start, finish)
start = finish
add_block(arr, :standby, finish, adj_plan[:start])
arr << [:plan, adj_plan]
finish = [adj_plan[:finish], adj_avail[:available][:finish]].max
add_block(arr, :available, adj_plan[:finish], finish)
add_block(arr, :standby, finish, adj_avail[:coverage][:finish])
restore_times(arr)
end
def adj_avail(avail)
avail.each_with_object({}) do |(k,g),h|
start, finish = g[:start], g[:finish]
h[k] = case k
when :coverage
{ start: start, finish: finish + (finish < start ? 24 : 0) }
else # when :available
{ start: start + (start < h[:coverage][:start] ? 24 : 0),
finish: finish + (finish < start ? 24 : 0) }
end
end
end
def adj_plan(avail, plan)
{ start: plan[:start] + (plan[:start] < avail[:coverage][:start] ? 24 : 0),
finish: plan[:finish] + (plan[:finish] < plan[:start] ? 24 : 0) }
end
def add_block(arr, value, curr_epoch, nxt_epoch)
arr << [value, { start: curr_epoch, finish: nxt_epoch }] if nxt_epoch > curr_epoch
end
def restore_times(arr)
arr.map! do |k,g|
start, finish = g.values_at(:start, :finish)
start -= 24 if start > 24
finish -= 24 if finish > 24
[k, { start: start, finish: finish }]
end
end
<强>实施例强>
avail = { coverage: { start: 3, finish: 18 },
available: { start: 3, finish: 12 } }
plan = { start: 6, finish: 15 }
categories(avail, plan)
#=> [[:available, {:start=>3, :finish=>6} ],
# [:plan, {:start=>6, :finish=>15} ],
# [:standby, {:start=>15, :finish=>18}]]
avail = { coverage: { start: 22, finish: 11 },
available: { start: 23, finish: 10 } }
plan = { start: 24, finish: 9 }
categories(avail, plan)
#=> [[:standby, {:start=>22, :finish=>23}],
# [:available, {:start=>23, :finish=>24}],
# [:plan, {:start=>24, :finish=>9 }],
# [:available, {:start=>9, :finish=>10}],
# [:standby, {:start=>10, :finish=>11}]]
avail = { coverage: { start: 1, finish: 13 },
available: { start: 2, finish: 3 } }
plan = { start: 4, finish: 12 }
categories(avail, plan)
#=> [[:standby, {:start=>1, :finish=>2 }],
# [:available, {:start=>2, :finish=>3 }],
# [:standby, {:start=>3, :finish=>4 }],
# [:plan, {:start=>4, :finish=>12}],
# [:standby, {:start=>12, :finish=>13}]]
<强>解释强>
这里的主要复杂因素是“覆盖范围”的完成时间。小于开始时间,意味着&#39;覆盖范围&#39;范围包含午夜。发生这种情况时,可以使用&#39;和&#39;计划&#39;范围也可能包含午夜。我通过在计算范围之前的24小时到午夜之后添加24小时,然后在计算范围后从超过24小时的所有小时值中减去24小时来解决这个问题。
考虑上面的第二个例子。
avail = { coverage: { start: 22, finish: 11 },
available: { start: 23, finish: 10 } }
adj_avail(avail)
#=> {:coverage=> {:start=>22, :finish=>35},
# :available=>{:start=>23, :finish=>34}}
plan = { start: 24, finish: 9 }
adj_plan(avail, plan)
#=> {:start=>24, :finish=>33}
如果我对categories
和avail
的这些值执行plan
,并且最后一行已注释掉,我会获得
a = categories(avail, plan)
#=> [[:standby, {:start=>22, :finish=>23}],
# [:available, {:start=>23, :finish=>24}],
# [:plan, {:start=>24, :finish=>33}],
# [:available, {:start=>33, :finish=>34}],
# [:standby, {:start=>34, :finish=>35}]]
和
restore_times(a)
#=> the return value shown above