перебирая ДВА разреженных матрицы - PullRequest
3 голосов
/ 25 августа 2009

Я использую форсированные разреженные матрицы, содержащие bool, и пытаюсь написать функцию сравнения для сохранения их на карте. Это очень простая функция сравнения. По сути, идея состоит в том, чтобы смотреть на матрицу как двоичное число (после сглаживания в вектор) и сортировать по значению этого числа. Это можно сделать следующим образом:

for(unsigned int j = 0; j < maxJ; j++)
{
  for(unsigned int i = 0; i < maxI; i++)
  {
    if(matrix1(i,j) < matrix2(i,j) return true;
    else if(matrix1(i,j) > matrix2(i,j) return false;
  }
}
return false;

Однако это неэффективно из-за разреженности матриц, и я хотел бы использовать итераторы для того же результата. Алгоритм, использующий итераторы, кажется простым, т.е. 1) возьмите первую ненулевую ячейку в каждой матрице, 2) сравните j * maxJ + i для обеих, 3) если равны, возьмите следующие ненулевые ячейки в каждой матрице и повторите. К сожалению, в коде это чрезвычайно утомительно, и я беспокоюсь об ошибках.

Что мне интересно, так это (а) есть ли лучший способ сделать это и (б) есть простой способ получить "следующую ненулевую ячейку" для обеих матриц? Очевидно, что я не могу использовать вложенные циклы for, как можно было бы перебирать одну разреженную матрицу.

Спасибо за вашу помощь.

-

Поскольку кажется, что алгоритм, который я предложил выше, может быть лучшим решением в моем конкретном приложении, я решил опубликовать код, который я разработал для сложной части, получая следующие ненулевые ячейки в двух разреженных матрицах. Этот код не идеален и не очень понятен, но я не уверен, как его улучшить. Если кто-то обнаружит ошибку или знает, как ее исправить, я был бы признателен за некоторые комментарии. В противном случае, я надеюсь, что это будет полезно для кого-то еще.

typedef boost::numeric::ublas::mapped_matrix<bool>::const_iterator1 iter1;
typedef boost::numeric::ublas::mapped_matrix<bool>::const_iterator2 iter2;

// Grabs the next nonzero cell in a sparse matrix after the cell pointed to by i1, i2.
std::pair<iter1, iter2> next_cell(iter1 i1, iter2 i2, iter1 end) const
{
    if(i2 == i1.end())
    {
        if (i1 == end)
            return std::pair<iter1, iter2>(i1, i2);
        ++i1;
        i2 = i1.begin();
    }
    else
    {
        ++i2;
    }

    for(; i1 != end;)
    {
        for(; i2 != i1.end(); ++i2)
        {
            return std::pair<iter1, iter2>(i1,i2);
        }
        ++i1;
        if(i1 != end) i2 = i1.begin();
    }
    return std::pair<iter1, iter2>(i1, i2);
}

Ответы [ 3 ]

1 голос
/ 28 августа 2009

Кстати, мне нравится этот вопрос.

Позвольте мне псевдокод из того, что я думаю, вы спрашиваете

declare list of sparse matrices ListA
declare map MatMAp with a sparse Matrix type mapping to a double, along with a
`StrictWeakMatrixOrderer` function which takes two sparse matrices.

Insert ListA into MatMap. 

Вопрос : Как эффективно написать StrictWeakMatrixOrderer?

Это подход. Я изобрел это на лету ....


Определите функцию flatten() и предварительно вычислите сглаженные матрицы, сохраняя сглаженные векторы в векторе (или другом контейнере со случайной индексацией верхней границы). flatten() может быть таким же простым, как объединение каждой строки (или столбца) с предыдущей (что можно сделать за линейное время, если у вас есть функция постоянного времени для захвата строки / столбца).

Это дает набор векторов с размером порядка 10 ^ 6. Это компромисс - сохранение этой информации вместо оперативного ее вычисления. Это полезно, если вы собираетесь делать много сравнений по ходу дела.

Помните, что нули содержат информацию - отбрасывание их может привести к двум векторам, равным друг другу, тогда как их порождающая матрица может не совпадать.

Затем мы преобразовали вопрос алгоритма из «матриц порядка» в «векторы порядка». Я никогда не слышал о метрике расстояния для матриц, но я слышал о метриках расстояния для векторов.

Вы можете использовать «сумму разностей», иначе говоря, расстояние Хэмминга. (элемент foreach, который отличается, добавьте 1). Это будет алгоритм O (n):

for i = 0 to max.
  if(a[i] != b[i])
     distance++

return distance

Расстояние Хэмминга удовлетворяет этим условиям

d(a,b) = d(b,a)
d(a,a) = 0
d(x, z) <= d(x, y) + d(y, z) 

Теперь, чтобы сделать какой-то анализ не по манере ....

  • 10^6 элементов в матрице (или соответствующем векторе).
  • O(n) Метрика расстояния.
    • Но это O(n) сравнений. Если у каждого доступа к массиву есть O(m) время, то у вас будет O(n*(n+n)) = O(n^2) метрика. Таким образом, вы have получите < O(n) доступ. Оказывается, что оператор std::vector [] предоставляет «амортизированный постоянный доступ к произвольным элементам» в соответствии с сайтом STI SGI.
  • Если у вас достаточно памяти для хранения k*2*10^6, где k - это количество управляемых матриц, это рабочее решение, которое использует много памяти в обмен на линейность.
0 голосов
/ 28 августа 2009

Мне кажется, мы говорим о реализации побитовых, поэлементных операторов на boost :: sparse_matrix, поскольку сравнение, если один вектор (или матрица) меньше другого, без использования каких-либо стандартных векторных норм, требует специальных операторов (или специальных отображений / нормы).

Насколько мне известно, не предоставляет специальных операторов для двоичных матриц (не говоря уже о разреженных двоичных матрицах). Маловероятно какие-либо прямые решения для этого с использованием матрицы уровня / векторной алгебры BLAS. Бинарные матрицы занимают свое место в поле линейной алгебры, поэтому есть хитрости и теоремы, но я сомневаюсь, что они проще, чем ваше решение.

Ваш вопрос может быть переформулирован следующим образом: Как эффективно отсортировать астрономически большие числа, представленные 2-битной картой (n = 100, так что элементы 100x100 дадут вам число, например 2 ^ 10000).

Хороший вопрос!

0 голосов
/ 27 августа 2009

(а) Я не совсем понимаю, что вы пытаетесь достичь, но если вы хотите сравнить, если обе матрицы имеют одинаковое значение с одинаковым индексом, достаточно использовать поэлементное умножение матриц (которое должно быть реализовано для так же разреженно):

matrix3 = element_prod (matrix1, matrix2);

Таким образом, вы получите для каждого индекса:

0 (false) * 1 (true) = 0 (false)
0*0 = 0
1*1 = 1

Таким образом, полученная матрица3 будет иметь ваше решение в одной строке:)

...