se.alipsa.groovy.stats.solver.GoalSeek.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of matrix-stats Show documentation
Show all versions of matrix-stats Show documentation
Statistical hypothesis tests based on Matrix data.
The newest version!
package se.alipsa.groovy.stats.solver
import org.apache.commons.math3.analysis.UnivariateFunction
import org.apache.commons.math3.analysis.solvers.AllowedSolution
import org.apache.commons.math3.analysis.solvers.BracketingNthOrderBrentSolver
import org.apache.commons.math3.analysis.solvers.UnivariateSolver
/**
Similar to the excel goal seek function.
*/
class GoalSeek {
/**
* Provides similar functionality to the excel/calc goal seek function.
* It uses an extension of the Brent-Dekker algorithm which uses inverse nth order polynomial
* interpolation instead of inverse quadratic interpolation, and which allows selection
* of the side of the convergence interval for result bracketing.
* Example usage
*
* def val = GoalSeek.solve(27000, 0, 100) {
* it * 100_000
* }
* println "Found the value ${val.value} after ${val.iterations} iterations"
* assert 0.270 == val.value
*
*
* @param targetValue the result we are after
* @param minValue the min value of the span to search for the value in
* @param maxValue the max value of the span to search for the value in
* @param threshold the allowed diff to still be considered as "no difference"
* @param maxIterations the maximum number of iterations allowed
* @param algorithm a closure containing the algoritm to apply
* @return a Map of the results with the following keys:
*
* - value: the actual value needed to produce the result
* - result: the result of the value put in the algorithm
* - diff: the difference from the target value
* - iterations: the number of iterations it took to get to the conclusion
*
* @throws RuntimeException if the goal cannot be found within the maxIterations
*/
static Map solve(final double targetValue, double minValue, double maxValue, double threshold = 1.0e-8, int maxIterations = 1000, Closure algorithm) {
if (minValue >= maxValue) {
throw new IllegalArgumentException("minValue ($minValue) must be smaller than maxValue ($maxValue)")
}
UnivariateFunction function = new UnivariateFunction() {
@Override
double value(double v) {
targetValue - algorithm.call(v)
}
}
// Recommended 1.0e-12 when a an absolute accuracy is 1.0e-8 so the divide by 10 000 to get a similar ratio
final double relativeAccuracy = threshold / 10_000
final double absoluteAccuracy = threshold
final int maxOrder = 5
UnivariateSolver solver = new BracketingNthOrderBrentSolver(relativeAccuracy, absoluteAccuracy, maxOrder);
double val = solver.solve(maxIterations, function, minValue, maxValue, AllowedSolution.LEFT_SIDE);
//[value: val, result: algorithm.call(val), diff: seeker.getDiff(currentValue), iterations: i]
double result = algorithm.call(val)
return [value: val, result: result, diff: (targetValue-result), iterations: solver.getEvaluations()]
}
}