假设我们有一个包含k个元素的有限域D = {d1,... dk}。
我们认为S是D ^ n的子集,即一组格式为< a1,..,an&gt ;,在D中有ai。
我们想要(紧凑地)使用S'(2 ^ D ^ n的子集)来表示它(紧凑地),即一组形式的元组< A1,...一个> Ai是D的子集。这意味着对于S中的任何元组s',Ai的叉积中的所有元素都存在于S中。
例如,考虑D = {a,b,c},因此k = 3,n = 2,并且元组S =< a,b> +< a,c> +< b,b> +< b,c>。
我们可以使用S'=< {a,b},{b,c}>代表S.
该单例解决方案也是最小的,S'=< {a},{b,c}> +< {b},{b,c}>也是一种解决方案,但它更大,因此不太理想。
在具体实例中,我们需要处理的一些大小:域D中的k~1000个元素,n <= 10相对较小(复杂性的主要来源),| S |范围大于&gt; 10 ^ 6。
一种天真的方法在于首先将S插入到S'2 ^ D ^ n的域中,然后使用以下测试,两个二,S'中的两个元组s1,s2可以融合以形成单个元组。 S'iff。它们只有一个组成部分不同。
e.g。
&LT; a,b&gt; +&lt; a,c&gt; - &GT; &LT; {A},{B,C}&GT; (第二部分不同)
&LT; b,b> +&lt; b,c> - &GT; &LT; {B},{B,C}&GT; (第二部分不同)
&LT; {A},{B,C}&GT; +&lt; {b},{b,c}&gt; - &GT; &LT; {A,B},{B,C}&GT; (第一部分不同)
现在可能有几个最小的S',我们有兴趣找到任何一个,并且某种最小化的近似值也可以,只要它们没有给出错误的结果(即使S'不是那么小)它可能会,但我们得到非常快的结果)。
朴素算法必须处理这样一个事实,即任何新引入的“融合”元组都可以与其他元组匹配,因此即使n保持较低,它在大型输入集上也会非常严重。你需要| S'| ^ 2比较以确保收敛,并且每当你融合两个元素时,我正在重新测试每一对(我怎么能改进它?)。
许多效率依赖于迭代顺序,因此以某种方式对集合进行排序可能是一个选项,或者可能是使用哈希进行索引,但我不确定如何进行。
命令式伪代码是理想的,或者将问题重新表述为我可以运行求解器的指针会非常有用。
答案 0 :(得分:2)
以下是一些伪造的(我尚未测试的C#代码),用于演示 S'=&lt; {a},{b,c}&gt; +&lt; {b},{b,c} &gt; 方法。除了空间要求,当使用元素的整数索引时可以忽略不计;添加和测试元组的整体效率和速度应该非常快。如果你想要一个实用的解决方案,那么你已经拥有了一个,你必须使用正确的ADT。
ElementType[] domain = new ElementType[]; // a simple array of domain elements
FillDomain(domain); // insert all domain elements
SortArray(domain); // sort the domain elements K log K time
SortedDictionary<int, HashSet<int>> subsets; // int's are index/ref into domain
subsets = new SortedDictionary<int, HashSet<int>>();
//
void AddTuple(SortedDictionary<int, HashSet<int>> tuples, ElementType[] domain, ElementType first, elementType second) {
int a = BinarySearch(domain, first); // log K time (binary search)
int b = BinarySearch(domain, second); // log K time (binary search)
if(tuples.ContainsKey(a)) { // log N time (binary search on sorted keys)
if(!tuples[a].Contains(b)) { // constant time (hash lookup)
tuples[a].Add(b); // constant time (hash add)
}
} else { // constant time (instance + hash add)
tuples[a] = new HashSet<in>();
tuples[a].Add(b);
}
}
//
bool ContainsTuple(SortedDictionary<int, HashSet<int>> tuples, ElementType[] domain, ElementType first, ElementType second) {
int a = BinarySearch(domain, first); // log K time (binary search)
int b = BinarySearch(domain, second); // log K time (binary search)
if(tuples.ContainsKey(a)) { // log N time (binary search on sorted keys)
if(tuples[a].Contains(b)) { // constant time (hash test)
return true;
}
}
return false;
}
优化元组子集S'的空间节省不会超过优化过程本身的减速。对于大小优化(如果你知道你的K小于65536,你可以在SortedDictionary和HashSet中使用短整数而不是整数。但即使是50 mil整数也只占用每32位整数4个字节* 50 mil~ = 200 MB 。
修改强> 这是通过将元组编码/映射到字符串的另一种方法,您可以利用二进制字符串比较以及UTF-16 / UTF-8编码非常有效的事实。同样,这仍然没有进行你想要的合并优化,但速度和效率会非常好。
这是JavaScript中的一些快速伪代码。
Array.prototype.binarySearch = function(elm) {
var l = 0, h = this.length - 1, i;
while(l <= h) {
i = (l + h) >> 1;
if(this[i] < elm) l = ++i;
else if(this[i] > elm) h = --i;
else return i;
}
return -(++l);
};
// map your ordered domain elements to characters
// For example JavaScript's UTF-16 should be fine
// UTF-8 would work as well
var domain = {
"a": String.fromCharCode(1),
"b": String.fromCharCode(2),
"c": String.fromCharCode(3),
"d": String.fromCharCode(4)
}
var tupleStrings = [];
// map your tuple to the string encoding
function map(tuple) {
var str = "";
for(var i=0; i<tuple.length; i++) {
str += domain[tuple[i]];
}
return str;
}
function add(tuple) {
var str = map(tuple);
// binary search
var index = tupleStrings.binarySearch(str);
if(index < 0) index = ~index;
// insert depends on tupleString's type implementation
tupleStrings.splice(index, 0, str);
}
function contains(tuple) {
var str = map(tuple);
// binary search
return tupleString.binarySearch(str) >= 0;
}
add(["a","b"]);
add(["a","c"]);
add(["b","b"]);
add(["b","c"]);
add(["c","c"]);
add(["d","a"]);
alert(contains(["a","a"]));
alert(contains(["d","a"]));
alert(JSON.stringify(tupleStrings, null, "\n"));