/** * Performs a non-linear - yet somewhat intelligent - search for the best values for the smoothing * coefficients alpha and beta for the given data set. * * <p>For the given data set, and models with a small, medium and large value of the alpha * smoothing constant, returns the best fit model where the value of the alpha and beta (trend) * smoothing constants are within the given tolerances. * * <p>Note that the descriptions of the parameters below include a discussion of valid values. * However, since this is a private method and to help improve performance, we don't provide any * validation of these parameters. Using invalid values may lead to unexpected results. * * @param dataSet the data set for which a best fit model is required. * @param modelMin the pre-initialized best fit model with the smallest value of the alpha * smoothing constant found so far. * @param modelMid the pre-initialized best fit model with the value of the alpha smoothing * constant between that of modelMin and modelMax. * @param modelMax the pre-initialized best fit model with the largest value of the alpha * smoothing constant found so far. * @param alphaTolerance the tolerance within which the alpha value is required. Must be * considerably less than 1.0. However, note that the smaller this value the longer it will * take to diverge on a best fit model. * @param betaTolerance the tolerance within which the beta value is required. Must be * considerably less than 1.0. However, note that the smaller this value the longer it will * take to diverge on a best fit model. This value can be the same as, greater than or less * than the value of the alphaTolerance parameter. It makes no difference - at least to this * code. */ private static TripleExponentialSmoothingModel findBest( DataSet dataSet, TripleExponentialSmoothingModel modelMin, TripleExponentialSmoothingModel modelMid, TripleExponentialSmoothingModel modelMax, double alphaTolerance, double betaTolerance) { double alphaMin = modelMin.getAlpha(); double alphaMid = modelMid.getAlpha(); double alphaMax = modelMax.getAlpha(); // If we're not making much ground, then we're done if (Math.abs(alphaMid - alphaMin) < alphaTolerance && Math.abs(alphaMax - alphaMid) < alphaTolerance) return modelMid; TripleExponentialSmoothingModel model[] = new TripleExponentialSmoothingModel[5]; model[0] = modelMin; model[1] = findBestBeta(dataSet, (alphaMin + alphaMid) / 2.0, 0.0, 1.0, betaTolerance); model[2] = modelMid; model[3] = findBestBeta(dataSet, (alphaMid + alphaMax) / 2.0, 0.0, 1.0, betaTolerance); model[4] = modelMax; for (int m = 0; m < 5; m++) model[m].init(dataSet); int bestModelIndex = 0; for (int m = 1; m < 5; m++) if (model[m].getMSE() < model[bestModelIndex].getMSE()) bestModelIndex = m; switch (bestModelIndex) { case 1: // Reduce maximums // Can discard models 3 and 4 model[3] = null; model[4] = null; return findBest(dataSet, model[0], model[1], model[2], alphaTolerance, betaTolerance); case 2: // Can discard models 0 and 4 model[0] = null; model[4] = null; return findBest(dataSet, model[1], model[2], model[3], alphaTolerance, betaTolerance); case 3: // Reduce minimums // Can discard models 0 and 1 model[0] = null; model[1] = null; return findBest(dataSet, model[2], model[3], model[4], alphaTolerance, betaTolerance); case 0: case 4: // We're done??? break; } // Release all but the best model constructed so far for (int m = 0; m < 5; m++) if (m != bestModelIndex) model[m] = null; return model[bestModelIndex]; }
/** * For the given value of the alpha smoothing constant, returns the best fit model where the value * of the beta (trend) smoothing constant is between betaMin and betaMax. This method will * continually try to refine the estimate of beta until a tolerance of less than betaTolerance is * achieved. * * <p>Note that the descriptions of the parameters below include a discussion of valid values. * However, since this is a private method and to help improve performance, we don't provide any * validation of these parameters. Using invalid values may lead to unexpected results. * * @param dataSet the data set for which a best fit model is required. * @param alpha the (fixed) value of the alpha smoothing constant to use for the best fit model. * @param betaMin the minimum value of the beta (trend) smoothing constant accepted in the * resulting best fit model. Must be greater than (or equal to) 0.0 and less than betaMax. * @param betaMax the maximum value of the beta (trend) smoothing constant accepted in the * resulting best fit model. Must be greater than betaMin and less than (or equal to) 1.0. * @param betaTolerance the tolerance within which the beta value is required. Must be * considerably less than 1.0. However, note that the smaller this value the longer it will * take to diverge on a best fit model. */ private static TripleExponentialSmoothingModel findBestBeta( DataSet dataSet, double alpha, double betaMin, double betaMax, double betaTolerance) { int stepsPerIteration = 10; if (betaMin < 0.0) betaMin = 0.0; if (betaMax > 1.0) betaMax = 1.0; TripleExponentialSmoothingModel bestModel = new TripleExponentialSmoothingModel(alpha, betaMin, 0.0); bestModel.init(dataSet); double initialMSE = bestModel.getMSE(); boolean betaImproving = true; double betaStep = (betaMax - betaMin) / stepsPerIteration; double beta = betaMin + betaStep; for (; beta <= betaMax || betaImproving; ) { TripleExponentialSmoothingModel model = new TripleExponentialSmoothingModel(alpha, beta, 0.0); model.init(dataSet); if (model.getMSE() < bestModel.getMSE()) bestModel = model; else betaImproving = false; beta += betaStep; if (beta > 1.0) betaImproving = false; } // If we're making progress, then try to refine the beta estimate if (bestModel.getMSE() < initialMSE && betaStep > betaTolerance) { // Can this be further refined - improving efficiency - by // only searching in the range beta-betaStep/2 to // beta+betaStep/2 ? return findBestBeta( dataSet, bestModel.getAlpha(), bestModel.getBeta() - betaStep, bestModel.getBeta() + betaStep, betaTolerance); } return bestModel; }