【问题标题】:How to get a lower-right part from sparse matrix?如何从稀疏矩阵中获得右下部分?
【发布时间】:2015-10-25 12:00:30
【问题描述】:

以“坐标格式”获得右下四分之一稀疏矩阵的最佳方法是什么?我需要它作为 Schur 补码。

坐标从 1 开始索引(我使用的是 MUMPS)。

示例:

int order_of_matrix = 4

int row_coords[] = {1,1,2,3,3,4};
int col_coords[] = {1,3,3,3,4,4};
int vals[]       = {1,2,3,4,5,6};

所以我的矩阵应该是这样的:

|1 0 2 0|
|0 0 3 0|
|0 0 4 5|
|0 0 0 6|

现在,我需要像这样从右下角获取值:

int schur_complement_size = 1
|6|

int schur_complement_size = 2
|4 5|
|0 6|

int schur_complement_size = 3
|0 3 0|
|0 4 5|
|0 0 6|

int schur_complement_size = 4
|1 0 2 0|
|0 0 3 0|
|0 0 4 5|
|0 0 0 6|

有没有什么优雅的方法可以在不将矩阵转换为密集格式的情况下实现这一点?密集格式对我来说很成问题,因为我的程序实际使用的矩阵会非常大。

【问题讨论】:

  • 我要做的第一件事是创建一个结构,这样您就不必为矩阵的不同部分保留三个大小相等的向量。然后,考虑新矩阵的现有矩阵的哪些部分以及元素的不同之处。最后,选择一种语言,因为答案会有所不同。
  • 我必须使用几个数组,这就是我之前在这个项目中使用它的方式。我在标题中插入了“C++”,所以现在更具体了。而且我不明白“考虑”的部分 - 我只是想提取矩阵的“右下”角......
  • 索引非零值的行向量和列向量是否保证被排序?如果是这样 - 如何?与蛮力方法相比,这肯定会改变“最优雅”的解决方案,因为您可以进行二进制搜索来找到起点。对于巨大的稀疏矩阵,加速可能很显着。
  • 好吧,如果你想避免使用三个不同的、动态分配的向量和随之而来的内存管理负担,那就这样吧。无论如何,从逻辑上讲,无论您如何存储它们,您仍然拥有三倍的行、列和值。我想让您考虑的是如何确定三元组是否是要从中提取的矩阵的一部分。然后,考虑在新矩阵中将使用哪个新三元组。

标签: c++ c matrix


【解决方案1】:

假设您无法更改稀疏矩阵的建模方式,您可以试试这个:

  • 确保 row_coords 是有序的(如您提供的示例中所示)。请注意,您也可以选择 col_coords,您需要的是两个数组之一是有序的。
  • 计算 q = order_of_matrix - order_of_submatrix + 1
  • 在已排序的数组 (row_coords) 中运行二分查找,找出第一个元素 row_coords[i]=q 的索引
  • 现在您可以使用 3 个新数组(您知道它们的最大大小)创建子矩阵并仅在 col_coords[i]>=q 时添加元素。

    sub_col_coords[j] = col_coords[i] - q + 1; sub_row_coords[j] = col_coords[i] - q + 1; sub_vals[j] = vals[i];

【讨论】:

  • 这个。对于大型数组,有一些结构是必不可少的。如果只是因为处理矩阵时缓存一致性增益 - 还因为它会显着减少主循环期间访问的元素数量
【解决方案2】:

对于您的任务,如果您不介意,我已经编写了一个工作示例并使用 std::vector 作为输出,因为动态分配的内存管理可能会变得复杂:

// Example program
#include <iostream>
#include <vector>

void GetSubSparseMatrixLowerRight(int order_of_matrix , int pointCount, int * row_coords, int *col_coords, int * vals, int schur_complement_size, std::vector<int> &row_coordsOut, std::vector<int> &col_coordsOut, std::vector<int> &valsOut)
{
    int upperLeftRow = order_of_matrix - schur_complement_size;
    int upperLeftCol = order_of_matrix - schur_complement_size;

    for(int i=0; i<pointCount; i++)
    {
        if(row_coords[i] > upperLeftRow && col_coords[i] > upperLeftCol)
        {
            row_coordsOut.push_back(row_coords[i] - upperLeftRow);
            col_coordsOut.push_back(col_coords[i] - upperLeftCol);
            valsOut.push_back(vals[i]);
        }
    }
}
int main()
{
  int order_of_matrix = 4;

  int row_coords[] = {1,1,2,3,3,4};
  int col_coords[] = {1,3,3,3,4,4};
  int vals[]       = {1,2,3,4,5,6};

  int schur_complement_size = 4;

  std::vector<int> row_coordsOut;
  std::vector<int> col_coordsOut;
  std::vector<int> valsOut;

  GetSubSparseMatrixLowerRight(order_of_matrix , sizeof(row_coords)/sizeof(row_coords[0]), (int *)row_coords, (int *)col_coords, (int *) vals, schur_complement_size, row_coordsOut,col_coordsOut, valsOut);

  std::cout<<valsOut[0]<<" "<<row_coordsOut[0]<<" "<<col_coordsOut[0]<<std::endl; //to control the output
}

为了简化函数参数,您可以定义一个结构或一个类来表示具有成员行坐标、列坐标和值的稀疏矩阵,如 cmets 中所述。尽管如此,这段代码还是完成了工作。

【讨论】:

  • 我担心push_back 在大型阵列的内存管理方面可能会变得非常低效 - 我建议预先分配大约 1/4N (+ 3 * sqrt(N)) 以显着加快速度。否则,这确实概述了未排序输入的基本方法。
  • @Floris 当然,reserve() 是不错的选择,但 std::vector 在到达边界时会保留这样的空间,乘数为 2。
【解决方案3】:

如果没有特殊的结构来存储非零条目,那么基本上 only 算法只是简单地迭代 all 的条目,复制你想要一个新的矩阵。

校样草图:您必须检查所有条目,否则您可能会错过相关条目;缺乏结构意味着你不能排除任何位置。

(漏洞:如果您的格式不包含重复条目,并且您已经阅读了足够多的条目以完全填充密集矩阵,那么您可以中止)

由于无论如何您都必须阅读每个条目,很难想象有任何算法可以比阅读它们并获取您想要的更好。

可能允许更快算法的“特殊结构”示例包括:条目以排序顺序(按坐标按字典顺序)存储,或者以其他一些更容易提取的巧妙顺序(例如,沿着空间填充曲线)存储块,或者你保留一个辅助数据结构来帮助这个操作。


可能会有一个有趣的问题,类似于“我可以使用什么数据结构来简化此操作?”但这需要有明确描述的要求。 (应该在不同的帖子中询问)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-31
    • 2019-06-03
    • 1970-01-01
    • 1970-01-01
    • 2012-01-10
    • 2018-01-19
    • 2017-03-31
    • 1970-01-01
    相关资源
    最近更新 更多