2013-05-17 64 views
7

セルフコーディング/インタビューのために、(任意のサイズの)行列の行列式を計算しようとしています。私の最初の試みは再帰を使用しているため、次の実装につながります。行列の行列式を計算する

import java.util.Scanner.*; 
public class Determinant { 

    double A[][]; 
    double m[][]; 
    int N; 
     int start; 
     int last; 

     public Determinant (double A[][], int N, int start, int last){ 
      this.A = A; 
      this.N = N; 
      this.start = start; 
      this.last = last; 
     } 

     public double[][] generateSubArray (double A[][], int N, int j1){ 
      m = new double[N-1][]; 
      for (int k=0; k<(N-1); k++) 
        m[k] = new double[N-1]; 

      for (int i=1; i<N; i++) 
      { 
        int j2=0; 
        for (int j=0; j<N; j++) 
        { 
         if(j == j1) 
           continue; 
         m[i-1][j2] = A[i][j]; 
         j2++; 
        } 
      } 
      return m; 
     } 
    /* 
    * Calculate determinant recursively 
    */ 
    public double determinant(double A[][], int N) 
    { 
     double res; 

     // Trivial 1x1 matrix 
     if (N == 1) 
      res = A[0][0]; 
     // Trivial 2x2 matrix 
     else if (N == 2) 
      res = A[0][0]*A[1][1] - A[1][0]*A[0][1]; 
     // NxN matrix 
     else 
     { 
      res=0; 
      for (int j1=0; j1<N; j1++) 
      { 
          m = generateSubArray (A, N, j1); 
          res += Math.pow(-1.0, 1.0+j1+1.0) * A[0][j1] * determinant(m, N-1); 
      } 
     } 
     return res; 
    } 
} 

これまでのところ、すべての点で問題なく、正しい結果が得られます。今度は、この行列式の値を計算するために複数のスレッドを使用してコードを最適化したいと考えています。 Java Fork/Joinモデルを使用して並列化しようとしました。これは私のアプローチです:

@Override 
     protected Double compute() { 
      if (N < THRESHOLD) { 
       result = computeDeterminant(A, N); 
       return result; 
      } 

      for (int j1 = 0; j1 < N; j1++){ 
       m = generateSubArray (A, N, j1); 
       ParallelDeterminants d = new ParallelDeterminants (m, N-1); 
       d.fork(); 
       result += Math.pow(-1.0, 1.0+j1+1.0) * A[0][j1] * d.join(); 
      } 

      return result; 
     } 

    public double computeDeterminant(double A[][], int N) 
    { 
     double res; 

     // Trivial 1x1 matrix 
     if (N == 1) 
      res = A[0][0]; 
     // Trivial 2x2 matrix 
     else if (N == 2) 
      res = A[0][0]*A[1][1] - A[1][0]*A[0][1]; 
     // NxN matrix 
     else 
     { 
      res=0; 
      for (int j1=0; j1<N; j1++) 
      { 
          m = generateSubArray (A, N, j1); 
          res += Math.pow(-1.0, 1.0+j1+1.0) * A[0][j1] * computeDeterminant(m, N-1); 
      } 
     } 
     return res; 
    } 

/* 
* Main function 
*/ 
public static void main(String args[]) 
{ 
    double res; 
      ForkJoinPool pool = new ForkJoinPool(); 
      ParallelDeterminants d = new ParallelDeterminants(); 
      d.inputData(); 
    long starttime=System.nanoTime(); 
      res = pool.invoke (d); 
    long EndTime=System.nanoTime(); 

    System.out.println("Seq Run = "+ (EndTime-starttime)/100000); 
    System.out.println("the determinant valaue is " + res); 
} 

は、しかし、性能を比較した後、私はフォークのパフォーマンスは/アプローチに参加非常に悪く、行列の次元が高いが、遅く、それは最初に比べて(なりことがわかりましたアプローチ)。オーバーヘッドはどこですか?誰もがこれを改善する方法について光を当てることができますか?

+0

スレッドを投げ込む前に、ループ内での割り当てをやめます。 1つのオプションは、Nの代わりにどの列と行を計算するかを決定する2つの配列パラメーターを持つことです。 –

+0

並列になるように設計されたアルゴリズムをお勧めします。私はあなたのアルゴリズムを通過しませんでしたが、私の経験では、一般的な問題のために多くの賢明な最適化を見つけることができます。 –

答えて

1

ForkJoinコードが遅い主な理由は、スローされたスレッドオーバーヘッドで実際にシリアル化されていることです。フォーク/ジョインを利用するには、まずすべてのインスタンスをforkしてから2)結果を待つ必要があります。 "計算"のループを2つのループに分割します:1つはfork(ParallelDeterminantsのインスタンスを配列に格納)し、もう1つは結果を収集します。

また、内側のものではなく、最も外側のレベルでのみフォークすることをお勧めします。あなたはO(N^2)スレッドを作成したくありません。

関連する問題