2013-05-17 7 views
7

Sto cercando di calcolare il determinante di una matrice (di qualsiasi dimensione), per la pratica di codifica/intervista. Il mio primo tentativo sta usando la ricorsione e che mi porta alla seguente applicazione:Determinante della matrice di calcolo

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

Finora è tutto buono e mi dà un risultato corretto. Ora vorrei ottimizzare il mio codice facendo uso di più thread per calcolare questo valore determinante. Ho provato a farlo parallelizzare usando il modello Java Fork/Join. Questo è il mio approccio:

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

Tuttavia dopo aver confrontato le prestazioni, ho trovato che le prestazioni della forcella/Join approccio è molto cattivo, e maggiore è la dimensione della matrice, più lenta diventa (come rispetto al primo approccio). Dov'è il sovraccarico? Qualcuno può far luce su come migliorare questo?

+0

Prima di inserire i thread, interrompo l'allocazione nel ciclo. Un'opzione potrebbe essere quella di avere due parametri dell'array che determinano quali colonne e righe calcolare invece di N. –

+0

Ti suggerirei di guardare alcuni algoritmi progettati per essere paralleli. Non ho seguito il tuo algoritmo ma nella mia esperienza si possono trovare molte intelligenti ottimizzazioni per problemi comuni cercando in giro. –

risposta

1

Il motivo principale per cui il codice ForkJoin è più lento è che in realtà è serializzato con un sovraccarico di thread generato. Per beneficiare di fork/join, è necessario 1) posizionare prima tutte le istanze, quindi 2) attendere i risultati. Dividi il tuo loop in "calcola" in due cicli: da uno a uno (memorizzando le istanze di ParallelDeterminants in, per esempio, un array) e un altro per raccogliere i risultati.

Inoltre, suggerisco di biforcarsi solo al livello più esterno e non in nessuno di quelli interni. Non vuoi creare thread O (N^2).