イベントのスケジュールにビット反転を使用できます。イベントの連続番号のバイナリ表現を取得し、そのビットを逆にして、結果を特定の範囲 (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 で初期化を続けますが、最初のイベントを取得するために 1 回繰り返します)。この修正により、反転ワードが 63 から 0 に減少し、常にイベントが可能な限り最大のスロットに分散され、最初の 60 回の反復で「競合する」イベントが許可されなくなります。
その他のアルゴリズム
前のアプローチは単純ですが、(いつでも) 最大の空のスロットが最小のスロットの 2 倍以下であることを保証するだけです。イベントをできるだけ離して配置したいので、フィボナッチ数または黄金比に基づくアルゴリズムが優先される場合があります。
- 初期間隔 (0..59) を優先度キュー (最大ヒープ、優先度 = 間隔サイズ) に配置します。
- イベントをスケジュールするには、優先キューをポップし、結果の間隔を黄金比 (1.618) で分割し、このイベントの時間として分割ポイントを使用し、2 つの結果の間隔を優先キューに戻します。
これにより、最大の空のスロットが最小のスロットの (約) 1.618 倍を超えないことが保証されます。小さいスロットの場合、近似は悪化し、サイズは 2:1 として関連付けられます。
スケジュールの変更間で優先キューを保持するのが不便な場合は、事前に 60 の可能なイベントの配列を準備し、新しいイベントが必要になるたびにこの配列から次の値を抽出できます。