【问题标题】:Sparse matrix storage in CC中的稀疏矩阵存储
【发布时间】:2011-03-29 02:34:09
【问题描述】:

我有一个非对称 I.E 的稀疏矩阵。稀疏性有点随机,我不能指望所有值都与对角线相距一定距离。

但是,它仍然是稀疏的,我想减少对矩阵的存储需求。因此,我试图弄清楚如何存储从第一个非零开始的每一行,直到我到达最后一个非零。

也就是说,如果第 m 行的第一个非零出现在第 2 列,最后一个非零出现在第 89 列,我想存储在 A[m] 行 2-> 89 中。

由于每一行没有相同数量的非零,我将让 A 的所有行具有相同数量的元素,并将零填充到具有较少非零数量的行的行尾-零个元素。

如何在 C 中进行这种翻译?我实际上没有原始的完整矩阵来复制值(原始矩阵以 CSR 形式出现在我身边)。如果我在 fortran 中这样做,我可以将我的数组定义为二维的,并且通过跟踪非零列的开始/停止值并像这样存储它,让每一行都是可变长度的。

我将尝试在下面演示:

这是我知道的值的矩阵表示 - 对于每个值,我知道行和列的位置

  [1    2    3    4                   ]
  [   5    6    7    8                ]
  [       10    11    12    13        ]
 m[   14    15    16    17       18   ]
  [         19    20    21         22 ]

现在这一行 m 在第一个非零和最后一个非零之间具有最大的“跨度” 所以我的新矩阵将是5x[span of row m]

  [1     2     3     4          ]
  [5     6     7     8          ]
  [10    11    12    13         ]
 m[14    15    16    17       18]
  [19    20    21       22      ] 

如您所见,行 m 不需要零填充,因为它是最长的“跨度”

其他行现在都将第 0 行作为第一个非零,并在每个非零之间保持零列的间距。

【问题讨论】:

    标签: c matrix sparse-matrix


    【解决方案1】:

    我会将它实现为一个参差不齐的数组,A[n][0] 总是返回对角线上的元素。 A[n][1] 将返回对角线右侧的项目,A[n][2] 将返回对角线左侧的项目,依此类推。然后,您只需要一个将矩阵索引 [i,j] 映射到不规则数组索引 [r][s] 的函数。

    这具有稀疏性的优点,如果您的值靠近对角线,则数组不会很长。


    或者,您可以有这样的定义:

    struct Row
    {
        int InitialOffset;
        int NumElements;
        int[] Values;
    }
    

    然后你会有一个 Row[]。根据矩阵索引检索值如下所示:

    //matrix is merely an array of rows...
    int GetValue(*matrix this, int i, int j)
    {
        Row CurrentRow = (*this)[i];
        if (CurrentRow.InitialOffset > j)
            return 0;
        else if (CurrentRow.InitialOffset + CurrentRow.NumElements < j)
            return 0; 
        return CurrentRow.Values[j - CurrentRow.InitialOffset]
    }
    

    我的 C 语法有点模糊,但你应该明白了。


    根据您的演示,我建议这样做:

    struct Matrix
    {
        int[,] Data
        int[] StartOffset;
        int[] NumberElements;
    }
    

    如下使用...

    int GetValue(*Matrix this, int i, int j)
    {
        if (this.StartOffset[i] > j)
            return 0;
        else if (this.StartOffset[i] + this.NumberElements[i] < j)
            return 0; 
        return this.Data[i, j-this.StartOffset[i]];
    }
    

    您的初始化过程如下所示

    //Data is a struct that holds row index, col index, and value
    Matrix* InitMatrix (*Data values, int numVals)
    {
        //loop through values to find longest row and number of rows
        //create new matrix, malloc matrix for longrow * numRows
        //malloc numrows elements for StartOffset and NumItems
        //foreach row, find min() and max()-min() of col indexs and 
        //store in StartOffset and NumItems
    }
    

    你需要做一些处理,但数据压缩并不便宜。

    【讨论】:

    • 可爱的索引想法。如果值接近对角线,应该会很有帮助。
    • 我之前已经实现了类似的东西。但是,由于 A[n][0] 处的元素实际上与我正在建模的物理结构中的 A[n+1][1] 的值相同,因此对 A 的任何更新(如在并行编程中)都是一种痛苦,因为我基本上必须重新打包整个矩阵,或者完全重新构建它。
    • Eep。我应该清除我以前的建议还是保留它们以供后代使用? (请参阅 #3 中的最新和最伟大的,应该按照您的图片所说的那样做)
    • @DonaldRay - 但在最坏的情况下,即使对于非常稀疏的矩阵,您仍然需要一个大小为 mxn 的数组。考虑一个仅填充第一列和最后一列的矩阵。它非常空,但您需要处理对角线左右两侧的所有元素才能到达边缘。还是我误解了你的建议?
    • @Donald:我通常把我的留在身边。大多数人不会对已被新信息过时的好建议投反对票。
    【解决方案2】:

    Derek,您在其中一个 cmets 中提到要使用单个 malloc。这意味着您知道有多少个非空元素。鉴于此, tt 可以将稀疏矩阵存储在一个数组中,该数组包含每个元素的矩阵元素的值和到下一个元素的“位置增量”。比如:

    struct melem {
        int value; // value of data
        int offset; // offset to next element
    }
    
    struct melem matrix[num_nonempty_elements];
    
    ...
    
    // Note: this is pseudocode!
    matrix[row*COLS + col].value = a[row][col];
    matrix[row*COLS + col].offset = (row*COLS + col)_[i] - (row*COLS + col)_[i-1];
    

    编辑:考虑一下,这与链表方法非常相似,但需要 1 次分配。 OTOH,它可能需要更多的计算才能访问所需的单元格。

    【讨论】:

      【解决方案3】:

      另一种方法是使用链接结构(如果矩阵非常稀疏,则非常有效,而不是因为填充得更多)。 I hinted at the implementation in a earlier answer.

      如果您打算使用连续运行实现,我不确定您是否真的想要/需要使用等长的行。为什么不使用参差不齐的数组?

      【讨论】:

      • 我想我不想使用参差不齐的数组是因为我想用一个 malloc 创建 A。我最终会做一个稀疏向量/矩阵乘法,我需要它尽可能高效地运行
      • 在这种情况下,您的问题似乎是在分配整个结构之前确定最大行宽之一。我认为这意味着您将不得不对数据文件进行两次传递。
      • 我更新了一些图形图片以帮助理解我想要完成的工作
      • 你想要的东西从一开始就很清楚。但是您需要一些您没有(或至少没有告诉我们)的信息来制作您想要的单个 malloc。也就是说:您需要知道表示行的最大长度(以及行数,尽管我一直假设您知道这一点)。这意味着两次传递您的数据。
      猜你喜欢
      • 2015-09-01
      • 1970-01-01
      • 2014-10-19
      • 1970-01-01
      • 2012-06-23
      • 2013-02-06
      • 2017-09-09
      • 2012-12-10
      • 1970-01-01
      相关资源
      最近更新 更多