2016-04-21 31 views
2

我計算Pearson correlation(平均用戶/項目評分)很多次,使用我當前的代碼的表現非常糟糕:性能陣列乘法皮爾遜

public double ComputeCorrelation(double[] x, double[] y, double[] meanX, double[] meanY) 
     { 
      if (x.Length != y.Length) 
       throw new ArgumentException("values must be the same length"); 

      double sumNum = 0; 
      double sumDenom = 0; 
      double denomX = 0; 
      double denomY = 0; 

      for (int a = 0; a < x.Length; a++) 
      { 
       sumNum += (x[a] - meanX[a]) * (y[a] - meanY[a]); 
       denomX += Math.Pow(x[a] - meanX[a], 2); 
       denomY += Math.Pow(y[a] - meanY[a], 2); 
      } 

      var sqrtDenomX = Math.Sqrt(denomX); 
      var sqrtDenomY = Math.Sqrt(denomY); 

      if (sqrtDenomX == 0 || sqrtDenomY == 0) return 0; 

      sumDenom = Math.Sqrt(denomX) * Math.Sqrt(denomY); 

      var correlation = sumNum/sumDenom; 

      return correlation; 
     } 

我使用與MathNet.Numerics標準Pearson相關,但這是修改標準,而且不可能使用它。有沒有辦法加快速度?如何優化時間複雜性?

+0

我覺得這是更好地問這個問題在這裏http://codereview.stackexchange.com/ – Sybren

+0

我們可以通過看代碼做一些假設,但我們什麼不知道這些假設實際上改善了性能。你應該通過一個分析器來運行它,以真正查看導致緩慢的原因。 –

回答

2

添加一些對MSE答案的全部計算能力 - 改變Pow(x,2)diff*diff,絕對是你想要做的事,你也可能希望避免不必要的束縛檢查你最內層的循環。這可以使用pointers in C#完成。

可以做到這樣:

public unsafe double ComputeCorrelation(double[] x, double[] y, double[] meanX, double[] meanY) 
    { 
     if (x.Length != y.Length) 
      throw new ArgumentException("values must be the same length"); 

     double sumNum = 0; 
     double sumDenom = 0; 
     double denomX = 0; 
     double denomY = 0; 
     double diffX; 
     double diffY; 

     int len = x.Length; 

     fixed (double* xptr = &x[0], yptr = &y[0], meanXptr = &meanX[0], meanYptr = &meanY[0]) 
     { 
      for (int a = 0; a < len; a++) 
      { 
       diffX = (xptr[a] - meanXptr[a]); 
       diffY = (yptr[a] - meanYptr[a]); 
       sumNum += diffX * diffY; 
       denomX += diffX * diffX; 
       denomY += diffY * diffY; 
      } 
     } 

     var sqrtDenomX = Math.Sqrt(denomX); 
     var sqrtDenomY = Math.Sqrt(denomY); 

     if (sqrtDenomX == 0 || sqrtDenomY == 0) return 0; 

     sumDenom = sqrtDenomX * sqrtDenomY; 

     var correlation = sumNum/sumDenom; 

     return correlation; 
    } 
1

解決您的性能問題的最佳方法可能是避免計算儘可能多的相關性。如果您將相關性用作另一種計算的一部分,則可以使用數學來消除其中一些計算的需要。

您還應該考慮您是否可以使用皮爾遜相關平方而不是皮爾遜相關本身。這樣,您可以將電話保存到Math.Sqrt(),這通常相當昂貴。

如果您確實需要取平方根,您應該再次使用sqrtDenomXsqrtDenomY,而不是重新計算平方根。

1

我在代碼中看到的唯一可能的優化是在以下代碼中,如果您仍然在尋找更好的性能,那麼您可能需要使用SIMD vectorization。它可以讓你使用的CPU

public double ComputeCorrelation(double[] x, double[] y, double[] meanX, double[] meanY) 
    { 
     if (x.Length != y.Length) 
      throw new ArgumentException("values must be the same length"); 

     double sumNum = 0; 
     double sumDenom = 0; 
     double denomX = 0; 
     double denomY = 0; 
     double diffX; 
     double diffY; 

     for (int a = 0; a < x.Length; a++) 
     { 
      diffX = (x[a] - meanX[a]); 
      diffY = (y[a] - meanY[a]); 
      sumNum += diffX * diffY; 
      denomX += diffX * diffX; 
      denomY += diffY * diffY; 
     } 

     var sqrtDenomX = Math.Sqrt(denomX); 
     var sqrtDenomY = Math.Sqrt(denomY); 

     if (sqrtDenomX == 0 || sqrtDenomY == 0) return 0; 

     sumDenom = sqrtDenomX * sqrtDenomY; 

     var correlation = sumNum/sumDenom; 

     return correlation; 
    }