【问题标题】:How to store a symmetric matrix?如何存储对称矩阵?
【发布时间】:2011-03-12 09:43:09
【问题描述】:

在内存中存储对称矩阵的最佳方式是什么?

在不影响速度和结构复杂性的情况下节省一半的空间会很好。这是一个与语言无关的问题,但如果您需要做出一些假设,只需假设它是一种很好的老式普通编程语言,如 C 或 C++..

如果有办法让事情变得简单,或者矩阵本身真的很大,这似乎是有意义的,对吗?

为了形式,我的意思是这个断言对于我想要存储的数据总是正确的

matrix[x][y] == matrix[y][x]

【问题讨论】:

标签: language-agnostic matrix


【解决方案1】:

这是一个存储对称矩阵的好方法,它只需要 N(N+1)/2 个内存:

int fromMatrixToVector(int i, int j, int N)
{
   if (i <= j)
      return i * N - (i - 1) * i / 2 + j - i;
   else
      return j * N - (j - 1) * j / 2 + i - j;
}

对于一些三角矩阵

0 1 2 3
  4 5 6
    7 8
      9

一维表示(例如存储在std::vector)如下所示:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

并且调用fromMatrixToVector(1, 2, 4)返回5,所以矩阵数据为vector[5] -> 5。

欲了解更多信息,请参阅http://www.codeguru.com/cpp/cpp/algorithms/general/article.php/c11211/TIP-Half-Size-Triangular-Matrix.htm

【讨论】:

  • Ben Axelrod 的文章(你引用的)有一些很好的三角数组的 ASCII 图表以及它在一维中的样子。也许您可以将它们复制到您的答案中?他们帮助我形象化了这一点。
  • 添加了矩阵可视化。
  • 第一个表达式可以改写为(2*N - i - 1)*i/2 + j
【解决方案2】:

如果你想使用一维数组,代码看起来像这样:

int[] new matrix[(rows * (rows + 1 )) >> 1];
int z;
matrix[ ( ( z = ( x < y ? y : x ) ) * ( z + 1 ) >> 1 ) + ( y < x ? y : x ) ] = yourValue; 

如果您创建一个额外的查找表,您可以摆脱乘法:

int[] new matrix[(rows * (rows + 1 )) >> 1];
int[] lookup[rows];
for ( int i= 0; i < rows; i++)
{
   lookup[i] = (i * (i+1)) >> 1;
}
matrix[ lookup[ x < y ? y : x ] + ( x < y ? x : y )  ] = yourValue;

【讨论】:

    【解决方案3】:

    我会尝试一个三角矩阵,像这样:

    int[][] sym = new int[rows][];
    for( int i = 0; i < cols; ++i ) {  
         sym=new int[i+1];
    }
    

    但是当有人想要访问“另一面”时,您将不得不面对这个问题。例如,他想访问 [0][10] 但在您的情况下,此 val 存储在 [10][0] 中(假设为 10x10)。

    可能“最好”的方式是懒惰的方式——在用户请求之前不要做任何事情。因此,如果用户键入 print(matrix[4]) 之类的内容,您可以加载特定行。

    【讨论】:

    • 不应该是sym[i] = new int[i + 1]吗?
    【解决方案4】:

    我发现很多高性能包只存储整个矩阵,然后只读取上三角或下三角。然后,他们可能会在计算期间使用额外的空间来存储临时数据。

    但是,如果存储确实是一个问题,那么只需将构成上三角形的 n(n+1)/2 元素存储在一维数组中即可。如果这让您的访问变得复杂,只需定义一组辅助函数即可。

    在 C 中访问矩阵 matA 你可以定义一个宏:

    #define A(i,j, dim) ((i <= j)?matA[i*dim + j]:matA[j*dim + i])
    

    那么您几乎可以正常访问您的数组。

    【讨论】:

    • 这似乎是一个很好的解决方案,让我在接受之前尝试一下:)
    • 我很难理解宏。宏是否用于访问一维数组中的元素?
    • @Rajath,我认为给定一个具有dim 行的方阵,A(i, j, dim) 返回一维数组中i,j 的索引。
    • 宏不准确,因为它可能会尝试访问定义范围 n(n+1)/2 之外的元素。
    • @Ale 宏是准确的,它总是从上三角子矩阵中读取。无意让它使用n(n+1)/2 存储。
    【解决方案5】:

    如果您的语言支持,您可以使用交错数组(或其他任何名称),并且当 x

    一个 n x n 矩阵的伪代码(有点 Python 风格,但不是真的):

    matrix[n][]
    
    for i from 0 to n-1:
        matrix[i] = some_value_type[i + 1]
    
    [next, assign values to the elements of the half-matrix]
    

    然后在引用值时......

    if x < y:
        return matrix[y][x]
    else:
        return matrix[x][y]
    

    【讨论】:

    • “引用值”是否应该在像这样一个返回值 getElement(x,y) 这样的包装函数中? (我不懂 Python)
    【解决方案6】:

    如果您正在使用支持运算符重载的东西(例如 C++),那么透明地处理它非常容易。只需创建一个矩阵类来检查两个下标,如果第二个大于第一个,则交换它们:

    template <class T>
    class sym_matrix { 
        std::vector<std::vector<T> > data;
    public:
        T operator()(int x, int y) {
            if (y>x)
                return data[y][x];
            else
                return data[x][y];
        }
    };
    

    目前我已经跳过了其他所有内容,只介绍了下标。实际上,要正确处理作为左值和右值的使用,您通常希望直接返回代理而不是 T。您需要一个将 data 创建为三角形的 ctor(即,对于 NxN 矩阵,第一行将有 N 个元素,第二行将有 N-1,依此类推——或者,等价于 1、2、.. .N)。您也可以考虑将 data 创建为单个 vector - 您必须计算正确的偏移量,但这并不是非常困难,而且它会使用更少的内存,运行得更快等等。我d 对第一个版本使用简单的代码,如果需要,稍后进行优化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-18
      • 1970-01-01
      • 1970-01-01
      • 2015-09-01
      • 1970-01-01
      • 2015-11-03
      • 1970-01-01
      相关资源
      最近更新 更多