将重叠序列展平为连续集

时间:2017-04-17 16:57:10

标签: ruby-on-rails ruby algorithm sequence overlap

对于Ruby on Rails规划应用程序,我遇到了一个我无法有效解决的算法/组合问题。

在我的应用程序中,我有两种类型的记录:

  • 可用性(何时可以免费使用,备用或明确无法使用(生病,休假))
  • 计划记录(实际安排的人在何时)。

两种类型的记录都是由开始和结束时间定义的,而且可用性还有其他类型(可用,待机,不可用)。

现在,我希望得到一个非重叠期间的详细列表,这些期间会显示某人当前有计划记录,但另外还有可用性

举个例子:

Time:  0-----------6-----------12-----------18-----------24
Avail:     |-----available-----||--standby--|
Plans:             |------------------|

Result:    |------||------------------||----|

期望的结果是3个非重叠期:

  • 3-6:可用
  • 6-15:计划
  • 15-18:待命

另一个需要拆分可用性的示例:

Time:  0-----------6-----------12-----------18-----------24
Avail:     |-----available-----||--standby--|
Plans:             |-----|

Result:    |------||-----||----||-----------|

期望的结果是3个非重叠期:

  • 3-6:可用
  • 6-9:计划
  • 9-12:可用
  • 12-18:待命

我已经在数组中拥有所有(重叠)句点。什么是有效实现我想要的最佳方式?

1 个答案:

答案 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}

如果我对categoriesavail的这些值执行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