(順序付けられていない) オブジェクトのセットがあります。オブジェクトの順序付きリストを取得し、そのリスト内に少なくとも 1 つの順序付き競合があった場合は true を返し、それ以外の場合は false を返すオラクルもあります。順序付けされた競合は、オラクルが任意の入力リスト [...、A、...、B、...] に対して true を返すようなオブジェクト (A,B) の順序付けられたペアです。(A,B) が順序付き競合であることは、(B,A) が順序付き競合であることを必ずしも意味しません。
セット内のすべての順序付けられていない競合を特定したい: つまり、(x, y) または (y, x) のいずれかが上で定義した順序付けられた競合であるようなすべてのペア {x, y} を見つけます。オラクルは遅い (呼び出しごとに数十ミリ秒から数百ミリ秒) ため、オラクル呼び出しの数を最小限に抑えることが不可欠です。明らかな素朴なアルゴリズム (セット要素のすべての可能な順序付けられたペアをオラクルに供給する; O(n²) 呼び出し) は受け入れられません。何百ものセット要素があり、全体で 10 未満の競合があると予想されます。
これは私が得た限りです。オラクルが2要素リストに対してtrueを返す場合、リスト内の要素は明らかに競合を構成します。オラクルがいずれかのリストに対して false を返す場合、そのリストには順序付けされた競合はありません。オラクルがリスト L とリスト L の反転の両方に対して false を返す場合、L には順序付けられていない競合はありません。
Put all the set elements in a list L (choose any convenient order).
Invoke the oracle on L.
If the oracle returns false, invoke the oracle on rev(L).
If the oracle again returns false, there are no unordered conflicts within L.
# At this point the oracle has returned true for either L or rev(L).
If L is a two-element list, the elements of L constitute an unordered conflict.
Otherwise, somehow divide the set in half and recurse on each
「セットを半分に分割して再帰する」部分で立ち往生しています。2 つの合併症があります。最初に、順序付けられたリストの上半分と下半分を取得するだけでは十分ではありません。これは、競合が分割によって解消される可能性があるためです ([...A1、A2、... An ... を検討してください)。 ][...B1、B2、...Bn ...])。サイズ n/2 のすべてのサブセットを列挙するとうまくいくはずですが、それを効率的に行う方法がわかりません。第 2 に、単純な再帰は、コール スタックの暗黙の状態のために大量の作業を繰り返す可能性があります。A が B と競合することを特定したとします。その場合、A と B の両方を含むリストを使用したオラクルの呼び出しは無駄になりますが、それでも他の競合 {A, x} および {B, x} を除外する必要があります。(A, B) が既にテストされている場合にのみ、M[a][b] が true になるようなメモ行列を維持できますが、そうではありません。
コンテキストによる追加の複雑さ: オブジェクトがリストに複数回表示される場合、2 番目以降のインスタンスは無視されます。さらに、一部のオブジェクトには依存関係があります。(P,Q) が依存関係である場合、Q が P の最初の出現 (存在する場合) の前に出現するオラクル入力は、誤って競合を報告します。このアルゴリズムが開始される前に、すべての依存関係がすでに特定されています。P が A と競合する場合、Q も A と競合するかどうかを知ることはできませんが、これは許容できる制限です。
(コンテキスト: これは、同じソース ファイルに含めることができない C システム ヘッダーのペアを識別するためのものです。「oracle」はコンパイラです。)