public static MedianPolish GetMedianPolish(double?[,] matrix, double epsilon, int maxIterations) { int rowCount = matrix.GetLength(0); int columnCount = matrix.GetLength(1); double overallConstant = 0; Vector <double> rowEffects = new DenseVector(rowCount); Vector <double> columnEffects = new DenseVector(columnCount); double?[,] residuals = (double?[, ])matrix.Clone(); double oldSum = 0; for (int iteration = 0; iteration < maxIterations; iteration++) { for (int iRow = 0; iRow < rowCount; iRow++) { double median = GetRow(residuals, iRow).Median(); SetRow(residuals, iRow, GetRow(residuals, iRow).Select(value => value - median)); rowEffects[iRow] += median; } double cDelta = columnEffects.Median(); columnEffects = columnEffects.Subtract(cDelta); overallConstant += cDelta; for (int iCol = 0; iCol < columnCount; iCol++) { double median = GetColumn(residuals, iCol).Median(); SetColumn(residuals, iCol, GetColumn(residuals, iCol).Select(value => value - median)); columnEffects[iCol] += median; } double rDelta = rowEffects.Median(); rowEffects = rowEffects.Subtract(rDelta); overallConstant += rDelta; double newSum = 0; foreach (var value in residuals) { newSum += Math.Abs(value.GetValueOrDefault()); } bool converged = newSum == 0 || Math.Abs(newSum - oldSum) < epsilon * newSum; if (converged) { break; } oldSum = newSum; } return(new MedianPolish { ColumnEffects = columnEffects.ToArray(), OverallConstant = overallConstant, Residuals = residuals, RowEffects = rowEffects.ToArray() }); }