假设我想在00:00-00:59期间安排一系列活动。我安排他们完整的分钟(00:01,从不00:01:30)。
我希望在那段时间内将它们尽可能地分开,但我事先并不知道在那个小时内我将会有多少事件。我今天可以安排一个活动,明天安排两个。
我脑子里有明显的算法,我可以想到实施它的蛮力方法,但我确信有人知道更好的方法。我更喜欢Ruby或者我可以翻译成Ruby的东西,但我会拿走我能得到的东西。
所以我能想到的算法是:
事件1最终在00:00结束。
事件2在00:30结束,因为该时间离现有事件最远。
第3项比赛可能会在00:15或00:45结束。所以也许我只选择第一个,00:15。
事件4然后在00:45结束。
事件5在00:08左右结束(从00:07:30向上舍入)。
等等。
所以我们可以看看每对分钟(比如,00:00-00:15,00:15-00:30,00:30-00:00),选择最大范围(00:30- 00:00),将它除以2和圆。
但我相信它可以做得更好。分享!
答案 0 :(得分:2)
由于您最多只能安排60个事件,因此我认为使用静态表值得一试(与思考算法相比并进行测试)。我的意思是对你来说,在一段时间内布置事件是一项非常简单的任务。但要告诉计算机如何做到这一点并不容易。
所以,我建议使用静态时间值来定义表,以便放置下一个事件。它可能是这样的:
00:00, 01:00, 00:30, 00:15, 00:45...
答案 1 :(得分:2)
您可以使用位反转来安排您的活动。只需获取事件序列号的二进制表示,反转其位,然后将结果缩放到给定范围(0..59分钟)。
另一种方法是按顺序生成位反转字(0000,1000,0100,1100,...)。
这样可以轻松分发多达32个事件。如果需要更多事件,在缩放结果后,应检查结果分钟是否已被占用,如果是,则生成并缩放下一个单词。
以下是Ruby中的示例:
class Scheduler
def initialize
@word = 0
end
def next_slot
bit = 32
while (((@word ^= bit) & bit) == 0) do
bit >>= 1;
end
end
def schedule
(@word * 60) / 64
end
end
scheduler = Scheduler.new
20.times do
p scheduler.schedule
scheduler.next_slot
end
按顺序生成位反转字的方法借用“Matters Computational ”,第1.14.3章。
<强>更新强>
由于从0..63缩放到0..59,这个算法倾向于在0,15,30和45之后产生最小的槽。问题是:它总是从这些(最小的)槽开始填充间隔,而从最大的插槽开始填充更自然。因此,算法并不完美。另外一个问题是需要检查“已经占用的分钟”。
幸运的是,一个小修补程序可以解决所有这些问题。只需改变
while (((@word ^= bit) & bit) == 0) do
到
while (((@word ^= bit) & bit) != 0) do
并使用63初始化@word
(或者将其初始化为0,但是执行一次迭代以获取第一个事件)。此修复将反转的字从63递减到零,它总是将事件分配到最大可能的插槽,并且在前60次迭代中不允许“冲突”事件。
其他算法
之前的方法很简单,但它只保证(在任何时刻)最大的空插槽不超过最小插槽的两倍。由于您希望尽可能远离事件,因此可能优先选择基于斐波纳契数或黄金比率的算法:
这可以保证最大的空插槽不超过(大约)最小插槽的1.618倍。对于较小的槽,近似值恶化,尺寸与2:1相关。
如果在计划更改之间保留优先级队列不方便,您可以提前准备一个包含60个可能事件的数组,并在每次需要新事件时从该数组中提取下一个值。
答案 2 :(得分:1)
由于您无法重新安排活动,并且您事先并不知道将会有多少活动,我怀疑您自己的建议(使用罗马的使用01:00的提示)是最好的。
但是,如果您对有多少事件达到最大值有任何估计,您可以对其进行优化。例如,假设您估计最多7个事件,您可以准备60 / (n - 1)
= 10分钟的时段并安排如下事件:
请注意,最后几个事件可能无法到达,所以00:50的使用概率很低。
这将是 fairer 然后基于非估计的算法,特别是在最坏情况下使用所有插槽:
答案 3 :(得分:0)
我写了一个我的解决方案的Ruby实现。它具有边缘情况,任何超过60的事件都将在0分钟叠加,因为现在每个可用空间都是相同的大小,并且它更喜欢第一个。
我没有说明如何处理60岁以上的事件,我并不在乎,但我认为随机化或循环法可以解决这种边缘情况。如果你关心的话。
each_cons(2)
gets bigrams;其余的可能很简单:
class Scheduler
def initialize
@scheduled_minutes = []
end
def next_slot
if @scheduled_minutes.empty?
slot = 0
else
circle = @scheduled_minutes + [@scheduled_minutes.first + 60]
slot = 0
largest_known_distance = 0
circle.each_cons(2) do |(from, unto)|
distance = (from - unto).abs
if distance > largest_known_distance
largest_known_distance = distance
slot = (from + distance/2) % 60
end
end
end
@scheduled_minutes << slot
@scheduled_minutes.sort!
slot
end
def schedule
@scheduled_minutes
end
end
scheduler = Scheduler.new
20.times do
scheduler.next_slot
p scheduler.schedule
end