我有一组(无序的)对象。我还有一个 oracle,它接受一个有序的对象列表,如果该列表中至少存在一个有序冲突,则返回 true,否则返回false。有序冲突是一对有序的对象 (A,B),使得 oracle 对任何输入列表 [..., A, ..., B, ...] 返回 true。(A,B) 是有序冲突并不一定意味着 (B,A) 是有序冲突。
我想识别集合中的所有无序冲突:也就是说,找到所有对 {x, y} 使得(x, y)或(y, x) 是上述定义的有序冲突。预言机很慢(每次调用几十到几百毫秒),因此必须尽量减少预言机调用的次数;显而易见的幼稚算法(将每个可能的有序集合元素对提供给 oracle;O(n²) 调用)是不可接受的。有数百个集合元素,预计总体冲突少于十个。
据我所知:如果 oracle 为一个二元素列表返回 true,那么显然列表中的元素构成冲突。如果 oracle 对任何列表返回 false ,则该列表中不存在有序冲突;如果 oracle 对列表 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
我被困在“将集合分成两半并递归”部分。有两个并发症。首先,取有序列表的上半部分和下半部分是不够的,因为冲突可能会通过拆分消除(考虑 [...A1, A2, ... An ... ][...B1,B2,...Bn ...])。枚举所有大小为 n/2 的子集应该可以工作,但我不知道如何有效地做到这一点。其次,由于调用堆栈上的隐式状态,一个简单的递归可能会重复大量工作——假设我们已经确定 A 与 B 冲突,那么任何包含 A 和 B 的列表的 oracle 调用都被浪费了,但我们仍然需要排除 {A, x} 和 {B, x} 的其他冲突。我可以维护一个备忘录矩阵,使得 M[a][b] 当且仅当 (A, B) 已经过测试时为真,但我没有
由于上下文的其他复杂性:如果任何对象在列表中出现多次,则忽略第二个和后续实例。此外,一些对象具有依赖关系:如果 (P,Q) 是依赖关系,那么在 P (如果有的话)第一次出现之前出现 Q 的任何 oracle 输入都会虚假地报告冲突。在此算法开始之前,所有依赖项都已被识别。如果 P 与 A 冲突,则无法知道 Q 是否也与 A 冲突,但这是可以接受的限制。
(上下文:这是用于识别不能包含在同一源文件中的 C 系统头文件对。“oracle”是编译器。)