Корзины с предохранителями для поиска классов эквивалентности

Предположим, что мы имеем конечную область D = {d1,.. dk}, содержащую k элементов.

Рассмотрим S подмножество D ^ n, т.е. множество наборов вида < a1,..., an > , ai в D.

Мы хотим представить его (компактно), используя S 'подмножество 2 ^ D ^ n, т.е. набор наборов вида < A1,.. An > , где 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} > также является решением, но оно больше, поэтому менее желательно.

Некоторые размеры, в конкретных случаях, которые нам нужно обрабатывать: k ~ 1000 элементов в области D, n <= 10 относительно мало (основной источник сложности), | S | до больших значений > 10 ^ 6.

Наивный подход заключается в первом погружении S в область S '2 ^ D ^ n, а затем, используя следующий тест, два на два, два набора s1, s2 в S' могут быть слиты с образованием одного кортежа в S 'iff. они отличаются только одним компонентом.

например.
< a, b > + < a, c > → < {a}, {b, c} > (отличаются на второй компонент)

< b, b > + < b, c > → < {b}, {b, c} > (различаются по второй компоненте)

< {a}, {b, c} > + < {b}, {b, c} > → < {a, b}, {b, c} > (отличаются на первой компоненте)

Теперь может быть несколько минимальных S ', нас интересует нахождение любого, и приближения некоторой минимизации тоже хорошо, если они не дают неправильных результатов (т.е. даже если S' не так мало, как это может быть, но мы получаем очень быстрые результаты).

Наивный алгоритм должен иметь дело с тем, что любой новый "плавленный" кортеж может соответствовать некоторому другому кортежу, поэтому он очень сильно масштабируется на больших наборах ввода, даже если n остается низким. Для обеспечения конвергенции вам нужны сравнения | S '| ^ 2, и в любой момент, когда вы сплавляете два элемента, я в настоящее время тестирую каждую пару (как я могу улучшить это?).

Большая эффективность зависит от порядка итераций, поэтому сортировка набора каким-либо образом может быть вариантом или, возможно, индексированием с использованием хэшей, но я не уверен, как это сделать.

Императивный псевдокод был бы идеальным, или указатели на переформулировку проблемы на то, что я могу запустить решатель, действительно помогли бы.

Ответы

Ответ 1

Здесь некоторый psuedo (код С#, который я не тестировал), который демонстрирует ваши S '= < {a}, {b, c} > + < {b}, {b, c} > . За исключением требований к пространству, которые при использовании целочисленного индекса для элемента незначительны; общая эффективность и скорость для добавления и тестирования кортежей должны быть очень быстрыми. Если вы хотите получить практическое решение, то у вас уже есть тот, который вам нужен только для использования правильных 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 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 занимают всего 4 байта на 32-битное целое * 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 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 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"));