融合元组以找到等价类

时间:2015-07-08 00:12:13

标签: algorithm

假设我们有一个包含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比较以确保收敛,并且每当你融合两个元素时,我正在重新测试每一对(我怎么能改进它?)。

许多效率依赖于迭代顺序,因此以某种方式对集合进行排序可能是一个选项,或者可能是使用哈希进行索引,但我不确定如何进行。

命令式伪代码是理想的,或者将问题重新表述为我可以运行求解器的指针会非常有用。

1 个答案:

答案 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"));