您可以使用位反转来安排您的活动。只需采用事件序列号的二进制表示,反转其位,然后将结果缩放到给定范围(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
按顺序生成位反转字的方法是从“计算事项
”第 1.14.3 章借用的。
更新:
由于从 0..63 缩放到 0..59,该算法倾向于在 0、15、30 和 45 之后生成最小的槽。问题是:它总是从这些(最小的)槽开始填充间隔,而它是从最大的插槽开始填充更自然。因此,算法并不完美。另一个问题是需要检查“已经占用的分钟”。
幸运的是,一个小修复解决了所有这些问题。只是改变
while (((@word ^= bit) & bit) == 0) do
至
while (((@word ^= bit) & bit) != 0) do
并@word
用 63 初始化(或继续用 0 初始化它,但进行一次迭代以获得第一个事件)。此修复将反转字从 63 减少到零,它始终将事件分配到最大可能的插槽,并且在前 60 次迭代中不允许“冲突”事件。
其他算法
前面的方法很简单,但它只能保证(在任何时候)最大的空槽不超过最小槽的两倍。由于您希望将事件尽可能分开,因此基于斐波那契数或黄金比例的算法可能是首选:
- 将初始间隔 (0..59) 放入优先级队列(最大堆,优先级 = 间隔大小)。
- 要安排一个事件,弹出优先级队列,以黄金比例 (1.618) 分割生成的间隔,使用分割点作为该事件的时间,并将两个生成的间隔放回优先级队列。
这保证了最大的空槽不超过(大约)最小槽的 1.618 倍。对于较小的插槽,近似值会变差,并且尺寸与 2:1 相关。
如果不方便在调度更改之间保留优先级队列,您可以提前准备一个包含 60 个可能事件的数组,并在每次需要新事件时从该数组中提取下一个值。