在c++中压缩对称矩阵

Compressing a symmetrical matrix in c++

本文关键字:对称 压缩 c++      更新时间:2023-10-16

假设我们有一个对称矩阵。

 A=
    1 2 3
    2 6 4
    3 4 5

现在,因为它是对称的,我们不需要存储记忆里面所有的数字。让我们假设在左下三角形的单元格中设置0。

B = 
    1 2 3
    0 6 4
    0 0 5

如果我想访问B的0元素的内容,我所需要做的就是反转感兴趣的单元格的行和col:

if(i>j) val += L[j][i]    // ex: B[1][0] is stored in B[0][1]

(假设目标是对所有未记忆的元素求和)

此时,我们只使用了右上角的三角形,但实际上并没有节省内存,因为未使用的元素仍然以0值分配。 节省内存的一种方法是使用vector of vector:
vector<vector<int>> C;

,并相应地调整每行的大小。

C=
  1 2 3
  6 4
  5

通过这样做,我们不能再使用交换技巧了,因为你可能会注意到空元素现在在矩阵的右下角三角形中。

则未分配的值为:

D=
   x x x
   x x 2
   x 3 4
在这种情况下,我们感兴趣的元素可以通过if条件找到:
if(j >= size - i)

现在的问题是确定0个元素的正确内容。换句话说:

if(j >= size - i) ans += L[?][?]

例如,如果我在i=1 j=2我不应该访问元素[1][2]而是[0][2]=2(以此类推[2][1]-> [0][2]= 3,[2][2]-> [1][1]= 4)

如何实现?

您的问题可以通过存储左下方的三角形并丢弃右上方的三角形来解决:

1
2 6
3 4 5

索引操作可以实现为:

int operator()(int r, int c) const { return r >= c ? L[r][c] : L[c][r]; }

如果你真的想以移位的方式存储矩阵的右上角三角形(见C),那么你可以这样访问矩阵元素:

int operator()(int r, int c) const { return c >= r ? L[r][c-r] : L[c][r-c]; }

然而,如果你真的想从你的压缩中获得一些利益,我建议把整个三角形打包成一个单一的一维向量。使用向量的向量会增加相当大的开销…

要将这样的三角形数组存储在压缩的线性向量中,我使用以下类。

#define ogf_array_check(index, data_size) assert(index < data_size)
#define ogf_assert(b) assert(b)
   /**
     * A 2d array of values aij where only the cells such 
     * that j <= i are represented.
     */
    template <class T> class TriangularArray {
    public:
        typedef TriangularArray<T> thisclass ;
        TriangularArray(int size) : size_(size), data_size_(size * (size + 1) / 2) {
            data_ = new T[data_size_] ;
        }
        ~TriangularArray() { delete[] data_; data_ = nil ; }
        int size() const { return size_ ; }
        int data_size() const { return data_size_ ; }
        /**
         * index0 denotes the line, and index1 the column,
         * note that index1 should be less-than or equal to
         * index0.
         */
        T& operator()(int index0, int index1) {
            ogf_array_check(index0, size_) ;
            ogf_array_check(index1, size_) ;
#ifdef OGF_ARRAY_BOUND_CHECK
            ogf_assert(index1 <= index0) ;
#endif
            int offset = index0 * (index0 + 1) / 2 ;
            return data_[offset + index1] ;
        }
        /**
         * index0 denotes the line, and index1 the column,
         * note that index1 should be lerr or equal to
         * index0.
         */
        const T& operator()(int index0, int index1) const {
            ogf_array_check(index0, size_) ;
            ogf_array_check(index1, size_) ;
#ifdef OGF_ARRAY_BOUND_CHECK
            ogf_assert(index1 <= index0) ;
#endif
            int offset = index0 * (index0 + 1) / 2 ;
            return data_[offset + index1] ;
        }
        void set_all(const T& value) {
            for(int i=0; i<data_size_; i++) {
                data_[i] = value ;
            }
        }
        T& from_linear_index(int index) {
            ogf_array_check(index, data_size_) ;
            return data_[index] ;
        }
        const T& from_linear_index(int index) const {
            ogf_array_check(index, data_size_) ;
            return data_[index] ;
        }
        T* data() const { return data_ ; }
    private:
        T* data_ ;
        int size_ ;
        int data_size_ ;
    private:
        TriangularArray(const thisclass& rhs) ;
        thisclass& operator=(const thisclass& rhs) ;
    } ;