在.NET中存储稀疏矩阵的最佳方法

时间:2009-04-16 14:18:06

标签: .net performance sparse-matrix

我们有一个存储稀疏矩阵的应用程序。该矩阵具有主要存在于矩阵的主对角线周围的条目。我想知道是否有任何有效的算法(或现有的库)可以有效地处理这种稀疏矩阵?优选地,这将是通用实现,其中每个矩阵条目可以是用户定义的类型。

编辑以回答问题/回复:

当我主要围绕主对角线说我的意思是大多数矩阵的特征是大多数条目聚集在主对角线之外但是可能存在靠近对角线的零并且可能存在非零值远离对角线。我想在这里为“大多数”案件提供一些有效的方法。

我将用它做什么?我需要能够有效地访问一行中的所有值或列中的所有值。存储的值将是布尔值。一个例子是:

  1. 对于连续的所有真值,foreach列中的true出现在将列的所有条目设置为
  2. 对于连续的所有错误值,请将条目设置为
  3. 之前已完成链接列表,但实施起来非常混乱。我希望使用稀疏矩阵我可以改进算法但是找到“正确”类型的稀疏矩阵算法已经证明是困难的。

    P.S。感谢到目前为止的回复

6 个答案:

答案 0 :(得分:8)

您可以使用基于单元格[row,col]的索引。由于数据在对角线上,因此将行索引和相关列的权限存储在数据中的典型方法不是最佳的。以下是您可以使用的一些代码:

    public class SparseMatrix<T>
    {
        public int Width { get; private set; }
        public int Height { get; private set; }
        public long Size { get; private set; }

        private Dictionary<long, T> _cells = new Dictionary<long, T>();

        public SparseMatrix(int w, int h)
        {
            this.Width = w;
            this.Height = h;
            this.Size = w * h;
        }

        public bool IsCellEmpty(int row, int col)
        {
            long index = row * Width + col;
            return _cells.ContainsKey(index);
        }

        public T this[int row, int col]
        {
            get
            {
                long index = row * Width + col;
                T result;
                _cells.TryGetValue(index, out result);
                return result;
            }
            set
            {
                long index = row * Width + col;
                _cells[index] = value;
            }
        }
    }

    static void Main()
    {
        var sm = new SparseMatrix<int>(512, 512);
        sm[42, 42] = 42;
        int val1 = sm[13, 13];
        int val2 = sm[42, 42];

        Console.WriteLine("VAL1 = " + val1); // prints out 0
        Console.WriteLine("VAL2 = " + val2); // prints out 42

        Console.ReadLine();
    }

请注意,当T是结构时,您可能必须调用IsCellEmpty,因为获取单元格的内容不会为null并且将具有该类型的默认值。您还可以展开代码,根据Size属性和_cells.Count为您提供快速的“SparseRatio”。

编辑:

好吧,如果你感兴趣的是速度,你可以做空间与速度的权衡。而不是只有一本字典,有三个!它使您的空间增加了三倍,但它可以让您想要以任何方式进行枚举。这是一些新代码,显示:

    public class SparseMatrix<T>
    {
        public int Width { get; private set; }
        public int Height { get; private set; }
        public long MaxSize { get; private set; }
        public long Count { get { return _cells.Count; } }

        private Dictionary<long, T> _cells = new Dictionary<long, T>();

        private Dictionary<int, Dictionary<int, T>> _rows = 
            new Dictionary<int, Dictionary<int, T>>();

        private Dictionary<int, Dictionary<int, T>> _columns = 
            new Dictionary<int, Dictionary<int, T>>();

        public SparseMatrix(int w, int h)
        {
            this.Width = w;
            this.Height = h;
            this.MaxSize = w * h;
        }

        public bool IsCellEmpty(int row, int col)
        {
            long index = row * Width + col;
            return _cells.ContainsKey(index);
        }

        public T this[int row, int col]
        {
            get
            {
                long index = row * Width + col;
                T result;
                _cells.TryGetValue(index, out result);
                return result;
            }
            set
            {
                long index = row * Width + col;
                _cells[index] = value;

                UpdateValue(col, row, _columns, value);
                UpdateValue(row, col, _rows, value);
            }
        }

        private void UpdateValue(int index1, int index2, 
            Dictionary<int, Dictionary<int, T>> parent, T value)
        {
            Dictionary<int, T> dict;
            if (!parent.TryGetValue(index1, out dict))
            {
                parent[index2] = dict = new Dictionary<int, T>();
            }
            dict[index2] = value;
        }
    }

如果要迭代所有条目,请使用_cells。如果您希望给定列的所有行都使用_columns。如果您希望给定行中的所有列都使用_rows

如果要按排序顺序进行迭代,可以开始将LINQ添加到混合中和/或使用带有内部类的排序列表,该内部类封装了一个条目(必须存储行或列并实现{{ 1}}用于排序工作)。

答案 1 :(得分:4)

我想Dictionary<int, Dictionary<int, object >>就足够了。

答案 2 :(得分:3)

这里有两个问题:

  • “大部分主要对角线周围”太模糊了。如果元素位于波段中,则使用波段本身的带状存储,因为向量偏离主对角线。如果元素在主对角线附近随机散射,则使用带状形式,可能在带中包含一些零,或者使用纯稀疏形式,仅存储元素及其在阵列中的位置。

  • 你会对矩阵做什么?如果您的目标仅仅是高效存储,那么带状表单将是高效的,可以快速访问任何元素。如果你将使用矩阵进行线性代数,但是从不超过矩阵向量乘法,那么带状形式仍然可以很好地工作。如果使用矩阵矩阵乘法或矩阵分解,填充成为问题,那么纯稀疏形式可能更合适。例如,两个带状矩阵的乘积将具有额外的带,因此两个三对角矩阵的乘积将是五对角的。对于因式分解,重新排序有时可用于最小化填充。 (AMD是一种选择,近似最小度排列,但还有其他方案。)

答案 3 :(得分:2)

我没有使用它,但是Nmath Matrix处理这些(不是免费的)。

另外,Extreme Optimization Numerical Libraries for .NET(不是免费的)。

以下是免费的:Math.NET Project(具体为MathNet.Numerics.LinearAlgebra.Sparse namespace

答案 4 :(得分:1)

我认为这可以通过使用一个包含普通数组的类来完成,保存矩阵行之间应用的水平偏移并定义一行的条带,例如:有效条目的数量。因此,对于仅定义了对角线和两个相邻元素的大矩阵,您将创建一个3 *行数的数组,并将3存储为条带宽度。偏移量取决于矩阵的大小。

我不知道有什么免费已经做到这一点。

答案 5 :(得分:1)

以下是一般data structure schemas的列表。每种都有其优点和缺点,适用于稀疏矩阵出现的略有不同的问题。您可能希望在现有数据结构之上实现它们,例如List&lt;&gt;和词典&lt;&gt;。