I am trying to make general class to calculate Geometric mean using exponential of the arithmetic mean of logarithms , and Arithmetic Mean.
I am looking for some general feedback on how I can improve the structure and efficiency of my code.
package analysis.statistic; import java.util.Arrays; public class Mean { /** * this function calculate geometric mean using the exponential of the * arithmetic mean of logarithms * * <ul> * <li>(-1)^m * 1/n-rt(product(numbers)) = (-1)^m exp(1/n * sum(ln(numbers[i]))) * <li>n : is length of numbers</li> * <li>m : is number of negative values</li> * </ul> * * @param numbers * @return geometric mean * <ul> * <li>NAN : if numbers array is empty</li> * <li>0 :if numbers array contain 0 value</li> * <li>negative value : if numbers array contains odd negative * values * <li>positive value : if numbers array contains even negative * values or just positive values * </ul> * * @throws IllegalArgumentException * if numbers array are null */ public static double geometricMean(int... numbers) { if (numbers == null) { throw new IllegalArgumentException("numbers must be not null"); } if (numbers.length == 0) { return Double.NaN; } int nNegativeValues = 0; double logarithmSum = 0; for (int i : numbers) { if (i > 0) { logarithmSum += Math.log(i); } else if (i < 0) { nNegativeValues += 1; logarithmSum += Math.log(i * -1); } else if (i == 0) { return 0; } } int length = numbers.length; return expOfArithMeanOfLogs(nNegativeValues, logarithmSum, length); } /** * Works just like {@link Mean#geometricMean(int...)} except the array * contains long numbers */ public static double geometricMean(long... numbers) { if (numbers == null) { throw new IllegalArgumentException("numbers must be not null"); } if (numbers.length == 0) { return Double.NaN; } int nNegativeValues = 0; double logarithmSum = 0; for (long i : numbers) { if (i > 0) { logarithmSum += Math.log(i); } else if (i < 0) { nNegativeValues += 1; logarithmSum += Math.log(i * -1); } else if (i == 0) { return 0; } } return expOfArithMeanOfLogs(nNegativeValues, logarithmSum, numbers.length); } /** * Works just like {@link Mean#Mean#geometricMean(int...)} except the array * contains double numbers */ public static double geometricMean(double... numbers) { if (numbers == null) { throw new IllegalArgumentException("numbers must be not null"); } if (numbers.length == 0) { return Double.NaN; } int nNegativeValues = 0; double logarithmSum = 0; for (double i : numbers) { if (i > 0) { logarithmSum += Math.log(i); } else if (i < 0) { nNegativeValues += 1; logarithmSum += Math.log(i * -1); } else if (i == 0) { return 0; } } return expOfArithMeanOfLogs(nNegativeValues, logarithmSum, numbers.length); } /** * Works just like {@link Mean#geometricMean(int...)} except the array * contains float numbers */ public static double geometricMean(float... numbers) { if (numbers.length == 0) { return Double.NaN; } int nNegativeValues = 0; double logarithmSum = 0; for (float i : numbers) { if (i > 0) { logarithmSum += Math.log(i); } else if (i < 0) { nNegativeValues += 1; logarithmSum += Math.log(i * -1); } else if (i == 0) { return 0; } } return expOfArithMeanOfLogs(nNegativeValues, logarithmSum, numbers.length); } /** * Return Exponential of the arithmetic mean of logarithms * * @param m * is the number of negative numbers * @param logarithmSum * is arithmetic mean of logarithms * @param n * numbers of values * @return exponential of the arithmetic mean of logarithms */ private static double expOfArithMeanOfLogs(int m, double logarithmSum, int n) { double expOfLogarithms = Math.exp(((double) 1 / n) * logarithmSum); if (m != 0) { expOfLogarithms = expOfLogarithms * Math.pow(-1, m); } return expOfLogarithms; } /** * The mean is the average of the numbers. * <li>sum(numbers[i])/n</li> * <li>n : length of array numbers</li> * * @param numbers * integers * @return average of the numbers * <ul> * <li>NAN : if array numbers is empty * <li>average : if contains numbers * </ul> * @throws IllegalArgumentException * if numbers array is null */ public static double arithmeticMean(int... numbers) { if (numbers == null) { throw new IllegalArgumentException("numbers must be not null"); } if (numbers.length == 0) { return Double.NaN; } return (double) Arrays.stream(numbers).parallel().sum() / numbers.length; } /** * Works just like {@link Mean#arithmeticMean(int...)} except the array * contains double numbers and * * @param numbers * double */ public static double arithmeticMean(double... numbers) { if (numbers == null) { throw new IllegalArgumentException("numbers must be not null"); } if (numbers.length == 0) { return Double.NaN; } return (double) Arrays.stream(numbers).parallel().sum() / numbers.length; } /** * Works just like {@link Mean#arithmeticMean(int...)} except the array * contains long numbers and * * @param numbers * long */ public static double arithmeticMean(long... numbers) { if (numbers == null) { throw new IllegalArgumentException("numbers must be not null"); } if (numbers.length == 0) { return Double.NaN; } return (double) Arrays.stream(numbers).parallel().sum() / numbers.length; } }
Integer.MIN_VALUE? \$\endgroup\$Integer.MIN_VALUEso i rewrite the function like blowpublic static double arithmeticMean(int... numbers) { long tmp = 0; for (int i : numbers) { tmp += i; } return (double) tmp / numbers.length; }\$\endgroup\$arithmeticMean(int...), and the only reason alongoverflow cannot occur now is that the size of an array is limited toInteger.MAX_VALUE, andInteger.MIN_VALUE * Integer.MAX_VALUE > Long.MIN_VALUE. Anyway, an overflow could also have occurred here withintvalues other thanInteger.MIN_VALUE. I think what @RolandIllig was actually referring to is the fact thatInteger.MIN_VALUEis the only value where your geometric mean method will fail, because it's the onlyintvalue whose additive inverse cannot be represented as anint. \$\endgroup\$Integer.MIN_VALUE? \$\endgroup\$Integer.MIN_VALUE(orLong.MIN_VALUE, respectively) is really a special case, you could throw anArithmeticException, thereby making it a part of your method's contract that an overflow can never occur. \$\endgroup\$