2009-04-16 24 views
8

疎行列を格納するアプリケーションがあります。この行列は、主に行列の主対角の周りに存在するエントリを持ちます。私はこの種の疎な行列を効率的に扱うことができる効率的なアルゴリズム(または既存のライブラリ)があるかどうか疑問に思っていましたか?好ましくは、これは、各マトリクスエントリがユーザ定義型であることができる一般的な実装であることが好ましい。質問/回答に応じて.NETに疎行列を格納する最も良い方法

編集:

私は主対角の周りにほとんど言うとき、私は行列のほとんどの特性は、ほとんどの項目は主対角のオフにクラスタ化されていることになるが、そこにできたことを意味対角線に近いゼロであり、対角線からはるかに離れた非ゼロ値が存在する可能性がある。私は、ここに「ほとんど」の事例を効率的にしたいと思っています。

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

行のすべての真の値について
  1. 、foreachの欄真が、何か行のすべての偽の値について
  2. に列のすべてのエントリを設定するものにエントリを設定するに表示されます。

これは以前はリンクリストで行われていましたが、実装するのは非常に混乱していました。スパース行列を使ってアルゴリズムを改善することができますが、「正しい」タイプのスパース行列アルゴリズムを見つけることは困難であることが分かっていました。

p.s.これまでの回答ありがとう

+0

私の回答が更新されました。スペース効率よりパフォーマンス効率が重要ですか?あなたは「スパース行列を効率的に処理する方法」と言い、ユースケースではデータへの複数のアクセス方法について話します。 –

+0

私はパフォーマンスがスペース効率よりも重要だと言います。とにかく非常に大量のデータを扱うので、行列が高速になる限り多くのスペースを使っても構いません。 –

答えて

7

セルの[row、col]に基づくインデックスを使用できます。データが対角線上にあるので、行インデックスおよび関連する列インデクスをデータで保存する典型的なアプローチは最適ではありません。ここではあなたがそれを行うために使用できるいくつかのコードは次のとおりです。Tが構造体であるとき、あなたは、セルの内容を取得して以来、IsCellEmptyを呼び出すために必要がある場合がありますことを

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(); 
    } 

注意がnullではありませんし、デフォルト値を持っていますそのタイプのSizeプロパティと_cells.Countに基づいた簡単な "SparseRatio"コードを展開することもできます。

EDIT:

さて、あなたは興味深い場合は、スピード対スペースのトレードオフを行うことができ、速度です。辞書を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>仕事を分類するため)。

+0

ありがとう、私はあなたがこれで行くところが好きです。辞書を使用しても行や列全体に効率的にアクセスできないのですか? (多分Linqを使って...?)。上記の私の編集を参照してください。 –

+0

別のオプションについては、アップデートを参照してください。スペースが問題でない場合は、複数の辞書を使用することでより高速にアクセスできるようにトレードオフを行ってください。 –

+0

素晴らしい提案、ありがとう –

4

私はDictionary<int, Dictionary<int, object >>は十分であろうと思います。

1

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

私はすでにこれを行う無料のものは何も知らない。

+0

良いアイデアです。 正の入力だけを仮定すると、負の数をエントリ間の0のエントリの数として扱うことができます。したがって、次のようになります... [1,2、-30,0,1,2、-29] に展開[1,2,0,0 ...] [0,1,2,0 ...] オフセットするには、配列[m * row + column]はmxn行列の(行、列)です –

1

ここには一般的なdata structure schemasのリストがあります。それぞれには長所と短所があり、まばらな行列が出現するわずかに異なる種類の問題に適しています。 List <>やDictionary <>のような既存のデータ構造の上に実装したいと思うかもしれません。

2

二つの質問がここにあります

  • 「ほとんどの主対角線の周りには、」あまりにも曖昧です。要素がバンド内にある場合は、バンド自体のバンドストレージを、メイン対角線からオフセットしたベクトルとして使用します。要素が主対角線の近くでランダムに散らばっている場合は、バンドにゼロを含むバンド形式を使用するか、配列内の要素とその位置だけを格納する純粋なスパースフォームを使用します。

  • あなたはマトリックスで何をしますか?あなたの目標が効率的なストレージだけであれば、帯状のフォームは効率的で、どの要素にも素早くアクセスできます。行列を使って線形代数を行いますが、行列ベクトルを乗算する以上の決してならない場合、帯状のフォームは引き続きうまく動作します。マトリックス行列の乗算または行列分解を使用して、塗りつぶしが問題になる場合は、純粋な疎フォームが適切な場合があります。例えば、2つのバンド行列の積は、追加のバンドを有するので、2つの3重対角行列の積は、五角形になる。因数分解の場合、フィルインを最小化するためにリオーダリングが役立つことがあります。 (AMDは1つの選択肢ですが、他の方式もあります)。

関連する問題