12

疎行列を格納するアプリケーションがあります。この行列には、主に行列の主対角線周辺に存在するエントリがあります。この種のスパース行列を効率的に処理できる効率的なアルゴリズム (または既存のライブラリ) があるかどうか疑問に思っていましたか? できれば、これは、各マトリックス エントリをユーザー定義型にできる一般的な実装にすることをお勧めします。

質問/回答に応じて編集:

ほとんどが主対角線の周りにあると言うとき、ほとんどの行列の特徴は、ほとんどのエントリが主対角線から離れてクラスター化されているが、対角線の近くにゼロがあり、遠く離れたゼロ以外の値がある可能性があるということです。対角線。ここで「ほとんどの」場合に効率的なものが必要です。

これを何に使うのでしょうか?行のすべての値または列のすべての値に効率的にアクセスできる必要があります。格納される値はブール値です。例は次のとおりです。

  1. 行のすべての真の値について、各列に真が表示され、列のすべてのエントリを何かに設定します
  2. 行のすべての false 値について、エントリを何かに設定します

これは以前はすべてリンクされたリストで行われていましたが、実装するのは非常に混乱していました。疎行列を使用してアルゴリズムを改善できることを期待していましたが、「適切な」タイプの疎行列アルゴリズムを見つけることは困難であることが判明しました。

ps 今までの反応ありがとう

4

6 に答える 6

10

セルの [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 が構造体の場合、セルの内容を取得すると null にはならず、その型のデフォルト値を持つため、IsCellEmpty を呼び出す必要がある場合があることに注意してください。Sizeコードを拡張して、プロパティ およびに基づいて簡単な「SparseRatio」を取得することもできます_cells.Count

編集:

速度に興味がある場合は、スペースと速度のトレードオフを行うことができます。辞書は 1 つだけではなく、3 つ持ってください。スペースが 3 倍になりますが、好きな方法で列挙するのが本当に簡単になります。以下は、次のことを示す新しいコードです。

    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 をミックスに追加するか、エントリをカプセル化する内部クラスで並べ替えられたリストを使用することができます (行または列を格納し、IComparable<T>並べ替えを機能させるために実装する必要があります)。 .

于 2009-04-16T15:04:19.247 に答える
4

Dictionary<int, Dictionary<int, object >>aで十分だと思います。

于 2009-04-16T14:25:47.857 に答える
3

私は使っていませんが、Nmath Matrixがこれらを処理します (無料ではありません)。

また、Extreme Optimization Numerical Libraries for .NET (無料ではありません)。

ここに無料のものがあります: Math.NET プロジェクト(具体的には MathNet.Numerics.LinearAlgebra.Sparse namespace )

于 2009-04-16T14:25:12.570 に答える
3

ここで 2 つの質問があります。

  • 「ほぼ主対角線のあたり」というのはあいまいすぎます。要素がバンド内にある場合は、主対角線からオフセットされたベクトルとして、バンド自体のバンド ストレージを使用します。要素が主対角線の近くにランダムに散らばっている場合は、バンドにいくつかのゼロを含む可能性のあるバンド形式を使用するか、配列内の要素とその位置のみを格納する純粋なスパース形式を使用します。

  • マトリックスどうする?目的が単に効率的なストレージである場合、バンド形式は効率的であり、あらゆる要素にすばやくアクセスできます。行列で線形代数を実行するが、行列ベクトルの乗算以上のことはしない場合でも、バンド形式は依然として見事に機能します。行列の行列乗算または行列の因数分解を使用する場合、埋め込みが問題になる場合は、純粋なスパース形式の方が適切な場合があります。たとえば、2 つの帯行列の積には追加の帯があるため、2 つの三重対角行列の積は五角形になります。因数分解の場合、フィルインを最小限に抑えるために並べ替えが役立つ場合があります。(AMD は 1 つの選択肢、近似最小次数順列ですが、他の方式もあります。)

于 2009-04-16T14:48:18.690 に答える
2

一般的なデータ構造スキーマのリストを次に示します。それぞれに長所と短所があり、スパース行列が発生するわずかに異なる種類の問題に適しています。List<> や Dictionary<> などの既存のデータ構造の上にそれらを実装することをお勧めします。

于 2009-04-16T14:33:47.807 に答える
1

これは、プレーン配列を保持するクラスを使用して、行列の行間に適用される水平オフセットを保存し、行のストライプ (有効なエントリの数など) を定義することで実行できると思います。したがって、対角要素と隣接する 2 つの要素のみが定義されている大きな行列の場合、3 * 行数の配列を作成し、ストライプ幅として 3 を格納します。オフセットは、行列のサイズによって異なります。

すでにこれを行っている無料のものは知りません。

于 2009-04-16T14:30:01.497 に答える