Ruby 如何从其他间隔中构建间隔数组
给定以下间隔数组:Ruby 如何从其他间隔中构建间隔数组,ruby,algorithm,data-structures,Ruby,Algorithm,Data Structures,给定以下间隔数组: today = Time.current.beginning_of_day tomorrow = Time.current.tomorrow.beginning_of_day availabilities = { monday: [ { start_time: today + 6.hours, end_of_time: today + 12.hours }, { start_time: today + 8.hours, end_o
today = Time.current.beginning_of_day
tomorrow = Time.current.tomorrow.beginning_of_day
availabilities = {
monday: [
{ start_time: today + 6.hours,
end_of_time: today + 12.hours },
{ start_time: today + 8.hours,
end_of_time: today + 18.hours }
],
tuesday: [
{ start_time: tomorrow + 10.hours,
end_time: tomorrow + 16.hours },
{ start_time: tomorrow + 18.hours,
end_time: tomorrow + 23.hours }
]
}
如何使用间隔构建可用性的数组,例如在星期一
和星期二
的情况下,哈希:
# monday
{ start_time: 'Today at 06:00',
end_time: 'Today at 18:00' }
# tuesday
[ { start_time: 'Tomorrow at 10:00',
end_time: 'Tomorrow at 16:00' },
{ start_time: 'Tomorrow at 18:00',
end_time: 'Tomorrow at 23:00' } ]
我想要实现的是获得给定日期的可用性间隔,而不管哪个实体将提供该可用性
请提前感谢,对于使用哪种算法的任何帮助或指导都将不胜感激
按开始时间对间隔进行排序
从第一个间隔开始,检查它是否与下一个间隔重叠
如果是,则合并它们并重复该过程
如果否,继续下一个间隔
证明其有效的证据:
如果对间隔A、B和C进行排序,并且A和C重叠,这也意味着B肯定与A重叠
这里有一种在Ruby中实现的方法
def overlap?(r1, r2)
!(r1.end <= r2.begin || r1.begin >= r2.end)
end
def merge_intervals(r1, r2)
[r1.begin, r2.begin].min..[r1.end, r2.end].max
end
def flatten_intervals(intervals)
first, *rest = intervals.sort_by(&:begin)
rest.each_with_object([first]) { |r,stack| stack <<
(overlap?(stack.last, r) ? merge_intervals(stack.pop, r) : r) }
end
intervals = [0..2, 5..8, 4..9, 11..13, 15..17, 19..21, 17..19, 16..20]
flatten_intervals(intervals)
#=> [0..2, 4..9, 11..13, 15..21]
def重叠?(r1、r2)
!(r1.end=r2.end)
结束
def合并_间隔(r1、r2)
[r1.begin,r2.begin].min..[r1.end,r2.end].max
结束
def展平间隔(间隔)
首先,*rest=interval.sort_by(&:begin)
rest.each_with_object([first]){| r,stack | stack[0..2,4..9,11..13,15..21]
这里有一种将时间间隔数组转换为非重叠时间间隔数组的简单方法。我假设粒度为1小时(但将其更改为分钟或秒很简单)。为方便起见,我还将时间间隔表示为范围,而不是问题中指定的哈希(尽管将范围转换为散列很容易)
假设
time_intervals = [0..2, 5..8, 4..9, 11..13, 15..17, 19..21, 17..19, 16..20]
我们可以按如下方式查看这些时间间隔:
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
xx xx xx xx xx xx xx xx xx
xx xx xx xx xx
e.entries
#=> [[0, 1, 2], [4, 5, 6, 7, 8, 9], [11, 12, 13], [15, 16, 17, 18, 19, 20, 21]]
我们希望将这些结合起来:
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
一个简单的方法如下
h = 24.times.with_object({}) { |i,h| h[i] = :uncovered }
time_intervals.each { |range|
(range.begin..range.end).each { |i| h[i] = :covered } }
h.delete_if { |_,v| v == :uncovered }.
keys.
slice_when { |k1, k2| k2 - k1 > 1 }.
map { |a| a.first..a.last }
#=> [0..2, 4..9, 11..13, 15..21]
h = 24.times.with_object({}) { |i,h| h[i] = :uncovered }
#=> {0=>:uncovered, 1=>:uncovered, 2=>:uncovered,..., 23=>:uncovered}
time_intervals.each { |range|
(range.begin..range.end).each { |i| h[i] = :covered } }
h #=> { all k=>:covered except k=>:uncovered for k = 3, 10, 14, 22 and 23 }
g = h.delete_if { |_,v| v == :uncovered }
#=> { all k=>:covered, k = 1,2, 4,5,6,7,8,9, 11,12,13, 15,16,17,18,19,20,21v]
k = g.keys
#=> [0, 1, 2, 4, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21]
e = k.slice_when { |k1, k2| k2 - k1 > 1 }
#=> #<Enumerator: #<Enumerator::Generator:0x007fee0a05c7b0>:each>
步骤如下
h = 24.times.with_object({}) { |i,h| h[i] = :uncovered }
time_intervals.each { |range|
(range.begin..range.end).each { |i| h[i] = :covered } }
h.delete_if { |_,v| v == :uncovered }.
keys.
slice_when { |k1, k2| k2 - k1 > 1 }.
map { |a| a.first..a.last }
#=> [0..2, 4..9, 11..13, 15..21]
h = 24.times.with_object({}) { |i,h| h[i] = :uncovered }
#=> {0=>:uncovered, 1=>:uncovered, 2=>:uncovered,..., 23=>:uncovered}
time_intervals.each { |range|
(range.begin..range.end).each { |i| h[i] = :covered } }
h #=> { all k=>:covered except k=>:uncovered for k = 3, 10, 14, 22 and 23 }
g = h.delete_if { |_,v| v == :uncovered }
#=> { all k=>:covered, k = 1,2, 4,5,6,7,8,9, 11,12,13, 15,16,17,18,19,20,21v]
k = g.keys
#=> [0, 1, 2, 4, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21]
e = k.slice_when { |k1, k2| k2 - k1 > 1 }
#=> #<Enumerator: #<Enumerator::Generator:0x007fee0a05c7b0>:each>
请参阅。也可以使用
最后一步是将数组转换为范围
e.map { |a| a.first..a.last }
#=> [0..2, 4..9, 11..13, 15..21]
合并重叠间隔的算法:
1. Sort the intervals on start time
2. Assign left and right of first interval (0)
3. Iterate over the intervals from 1 to size-1
if (current interval start lies in prev. interval)
update right to max(prev. right, current. right)
else
[left, right] is non-overlapping interval => push it to answer array
reassign left and right to current interval
4. push last [left, right] to answer array
解决方案:
# hash of overlapping intervals
availabilities = {
monday: [
{ start_time: 6,
end_time: 12 },
{ start_time: 8,
end_time: 18 }
],
tuesday: [
{ start_time: 10,
end_time: 16 },
{ start_time: 18,
end_time: 23 }
]
}
# function for converting hash to intervals, process, and then convert back to hash
def solve(list)
return_hash = {}
list.each do |key, arr|
intervals = []
arr.each { |hash| intervals << [hash[:start_time], hash[:end_time]] }
non_overlapping_intervals = merge_interval(intervals)
temp = []
non_overlapping_intervals.each { |interval| temp << {start_time: interval[0], end_time: interval[1]} }
return_hash[key] = temp
end
return_hash
end
# algorithm to merge intervals and return non-overlapping intervals
def merge_interval(v)
intervals = []
v.sort()
size = v.size()
l, r = v[0][0], v[0][1]
(1...size).each do |i|
if v[i][0] <= r
r = [r,v[i][1]].max;
else
intervals << [l, r]
l, r = v[i][0], v[i][1]
end
end
intervals << [l, r]
return intervals
end
# solve call for availabilities hash
p solve(availabilities)
使用稍微不同的格式(一个范围,而不是具有不一致键的哈希),此问题变得更容易:
today = 0
tomorrow = 24
availabilities = {
monday: [
{ start_time: today + 6,
end_time: today + 12 },
{ start_time: today + 8,
end_time: today + 18 }
],
tuesday: [
{ start_time: tomorrow + 10,
end_time: tomorrow + 16 },
{ start_time: tomorrow + 18,
end_time: tomorrow + 23 }
]
}
availabilities = availabilities.map do |day, avails|
[ day,
avails.map do |avail|
(avail[:start_time]..avail[:end_time])
end
]
end.to_h
# {:monday=>[6..12, 8..18], :tuesday=>[34..40, 42..47]}
然后,您可以使用来对范围进行求和。时间间隔=[0..2,3..4]
将忽略2和3之间的暂停。@Eric,这就是我的意图,因为我将0..2
和3..4
视为小时的集合([0,1,2]
和[3,4]
)而不是连续区间的表示。然而,我并不特别喜欢这个解决方案。恐怕它没有回答这个问题。优秀的算法:高效和容易理解。代码本来是好的,但是我认为省略。Ekkima,请随意编辑或删除我添加的代码。它是一个很好的A。回答:我想一些代码可以把它完善。