これを解決する1つの方法は、累積合計を含む二分探索木がどのように構築されるかを再考することです。二分探索木を構築するのではなく、各ノードを次のように解釈することを検討してください。
- 各ノードには、ノード自体専用の値の範囲が格納されます。
- 左側のサブツリーのノードは、その範囲のすぐ左側にある確率分布からのサンプリングを表します。
- 右側のサブツリーのノードは、その範囲のすぐ右側にある確率分布からのサンプリングを表します。
たとえば、イベントA、B、C、D、E、F、およびGの重みが3、2、2、2、2、1、および1であるとします。このバイナリツリーは、A、B、C、 D、E、F、およびG:
D
/ \
B F
/ \ / \
A C E G
ここで、ツリーに確率で注釈を付けます。A、C、E、およびGはすべて葉であるため、それぞれに確率質量1を与えます。
D
/ \
B F
/ \ / \
A C E G
1 1 1 1
ここで、Bのツリーを見てください。Bには重み2が選択され、Aには重み3が選択され、Cには確率2が選択されます。これらを[0、1)の範囲に正規化すると、Aは確率の3/7を占め、BとCはそれぞれ2/7を占めます。したがって、Bのノードは、範囲[0、3/7)のすべてが左側のサブツリーに移動し、範囲[3 / 7、5/7)のすべてがBにマップされ、範囲[5 / 7、1)適切なサブツリーにマップします。
D
/ \
B F
[0, 3/7) / \ [5/7, 1) / \
A C E G
1 1 1 1
同様に、Fを処理してみましょう。Eには選択の重み2があり、FとGにはそれぞれ選択の確率重み1があります。したがって、ここではEのサブツリーが確率質量の1/2を占め、ノードFが1/4を占め、Gのサブツリーが1/4を占めます。これは、確率を次のように割り当てることができることを意味します
D
/ \
B F
[0, 3/7) / \ [5/7, 1) [0, 1/2) / \ [3/4, 1)
A C E G
1 1 1 1
最後に、ルートを見てみましょう。左側のサブツリーの合計の重みは3+2 + 2=7です。右側のサブツリーの合計の重みは2+1 + 1 = 4です。したがって、D自体の重みは2です。したがって、左側のサブツリーの確率は7/13です。選択されると、Dは選択される確率が2/13になり、右側のサブツリーは選択される確率が4/13になります。したがって、確率を次のように確定できます。
D
[0, 7/13) / \ [9/13, 1)
B F
[0, 3/7) / \ [5/7, 1) [0, 1/2) / \ [3/4, 1)
A C E G
1 1 1 1
ランダムな値を生成するには、次の手順を繰り返します。
- ルートから開始:
- [0、1)の範囲で均一にランダムな値を選択します。
- 左側のサブツリーの範囲内にある場合は、そのサブツリーに降ります。
- 右側のサブツリーの範囲内にある場合は、そのサブツリーに降りてください。
- それ以外の場合は、現在のノードに対応する値を返します。
ツリーが構築されるときに、確率自体を再帰的に決定できます。
- リーフノードの左右の確率は0です。
- 内部ノード自体の重みがW、左側のツリーの合計の重みがW L、右側のツリーの合計の重みがW Rの場合、左側の確率は(W L)/(W + W L + W R)、右側の確率は(W L)/(W + W L + W R)です。確率は(W R)/(W + W L + W R)です。
この再定式化が役立つ理由は、更新された確率ごとにO(log n)時間で確率を更新する方法を提供するためです。特に、特定のノードの重みを更新した場合にどの不変条件が変化するかを考えてみましょう。簡単にするために、今のところノードがリーフであると仮定しましょう。リーフノードの重みを更新しても、確率はリーフノードに対しては正しいですが、そのノードのサブツリーの1つの重みが変更されているため、そのすぐ上のノードに対しては正しくありません。したがって、上記と同じ式を使用するだけで、(O(1)時間で)親ノードの確率を再計算できます。ただし、サブツリーの重みの1つが変更されたため、そのノードの親は正しい値を持たなくなり、そこで確率を再計算することもできます。このプロセスは、ツリーのルートまでさかのぼって繰り返され、レベルごとにO(1)計算を実行して、各エッジに割り当てられた重みを修正します。したがって、ツリーのバランスが取れていると仮定すると、1つの確率を更新するためにO(log n)の合計作業を実行する必要があります。ノードがリーフノードでない場合、ロジックは同じです。ツリーのどこかから始めます。
要するに、これは
- O(n)ツリーを構築する時間(ボトムアップアプローチを使用)、
- O(log n)ランダムな値を生成する時間、および
- O(log n)任意の1つの値を更新する時間。
お役に立てれば!