预测算法 | Holt Winter季节性指数平滑法 附JAVA代码

简介: 预测算法 | Holt Winter季节性指数平滑法 附JAVA代码

最近课上老师让我们做一个销量预测的作业,刚好小编被分配到了这个算法,所以在这里写一下,mark一下。


Holt Winter季节性指数平滑本质上就是三次指数平滑法,添加了一个新的参数c来表示平滑后的趋势。


Holt Winter季节性指数平滑有累加和累乘两种方法。


下面是累加的三次指数平滑:


微信图片_20220421155228.png


累加模型适用于具有线性趋势且季节效应不随时间变化的序列。


累乘式季节性对应的等式为:


微信图片_20220421155231.png


累乘模型适用于具有线性趋势且季节效应随序列量级发生变化的序列。


其中c_t为周期性的分量,代表周期的长度,F_{t+m}为模型预测的等式。s,b,t代表着水平、趋势和季节。


初始值的选取对于算法整体的影响不是特别大,通常的取值为s_0=x_0,b_0=x_1-x_0,累加时c_0=0,累乘时c_0=1。


α,ß,γ的值都位于[0,1]之间。


代码:代码下载移步留言区。

import java.util.ArrayList;
import java.util.List;
/**
 * 三次指数平滑预测,最多外推一个周期
 * @author Administrator
 *
 */
public class HoltWintersTripleExponentialImpl {
    static double[] alphas=new double[]{0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9};
    public static Double getbetterforecast(List<Integer> y, int period, int m) {
        double[] result=betterforecast(y,period,m);
        return result[result.length-1];
    }
    /**
     * 求出最佳预测
     * @param y
     * @param alpha
     * @param beta
     * @param gamma
     * @param period
     * @param m
     * @param debug
     * @return
     */
    public static double[] betterforecast(List<Integer> y, int period, int m) {
        double[] result = null;
        double avg=0;
        for(int i=0;i<alphas.length;i++){
            int sum=0;
            double[] temp=forecast(y,alphas[i],0.25,0,period,m,false);
            for(int j=0;j<y.size();j++){
                sum+=Math.abs(y.get(j)-temp[j]);
            }
            if(i==0){
                avg=sum/y.size();
                result=temp;
            }
            if(avg<(sum/y.size())){
                result=temp;
            }
        }
        return result;
    }
    /**
     * This method is the entry point. It calculates the initial values and returns the forecast
     * for the m periods.
     * @param y 数据集合
     * @param alpha - Exponential smoothing coefficients for level, trend, seasonal components.
     * @param beta - Exponential smoothing coefficients for level, trend, seasonal components.
     * @param gamma - Exponential smoothing coefficients for level, trend, seasonal components.
     * @param perdiod - 一个完整周期的长度,至少需要两个周期 
     * @param m -预测的个数
     * @param debug - 是否打印详细信息
     */
    public static double[] forecast(List<Integer> y, double alpha, double beta,
            double gamma, int period, int m, boolean debug) {
        if (y == null) {
            return null;
        }
        //获取季节数
        int seasons = y.size() / period;
        //平滑值st0
        double a0 = calculateInitialLevel(y, period);
        //bt0
        double b0 = calculateInitialTrend(y, period);
        //初始化季节因子
        double[] initialSeasonalIndices = calculateSeasonalIndices(y, period, seasons);
        if (debug) {
            System.out.println(String.format(
                    "Total observations: %d, Seasons %d, Periods %d", y.size(),
                    seasons, period));
            System.out.println("Initial level value a0: " + a0);
            System.out.println("Initial trend value b0: " + b0);
            printArray("Seasonal Indices: ", initialSeasonalIndices);
        }
        double[] forecast = calculateHoltWinters(y, a0, b0, alpha, beta, gamma,
                initialSeasonalIndices, period, m, debug);
        if (debug) {
            printArray("Forecast", forecast);
        }
        return forecast;
    }
    /**
     * This method realizes the Holt-Winters equations.
     * 计算进行预测
     * @param y
     * @param a0
     * @param b0
     * @param alpha
     * @param beta
     * @param gamma
     * @param initialSeasonalIndices
     * @param period
     * @param m
     * @param debug
     * @return - Forecast for m periods.  
     */
    private static double[] calculateHoltWinters(List<Integer> y, double a0, double b0, double alpha,
            double beta, double gamma, double[] initialSeasonalIndices, int period, int m, boolean debug) {
        //整体平滑 st=alpha * y [i] / It [i-period] +(1.0  -  alpha)*(St [i  -  1] + Bt [i  -  1])
        double[] St = new double[y.size()];
        //趋势平滑 Bt [i] = gamma *(St [i] -St [i-1])+(1-gamma)* Bt [i-1]
        double[] Bt = new double[y.size()];
        //季节性平滑It [i] = beta * y [i] / St [i] +(1.0  -  beta)* It [i  -  period]
        double[] It = new double[y.size()];
        //预测Ft [i + m] =(St [i] +(m * Bt [i]))* It [i  -  period + m]
        double[] Ft = new double[y.size() + m];
        //Initialize base values  初始化
        St[1] = a0;
        Bt[1] = b0;
        //季节性平滑
        for (int i = 0; i < period; i++) {
            It[i] = initialSeasonalIndices[i];
        }
        //预测初始化
        Ft[0+m] = (St[0] + (m * Bt[0])) * It[0];//因为Bt [0] = 0,所以这实际上是0
        Ft[m + 1] = (St[1] + (m * Bt[1])) * It[1];//Forecast starts from period + 2
        //Start calculations
        for (int i = 2; i < y.size(); i++) {
            //Calculate overall smoothing  计算所有的平滑值
            if((i - period) >= 0) {
                St[i] = alpha * y.get(i) / It[i - period] + (1.0 - alpha) * (St[i - 1] + Bt[i - 1]);
            } else {
                St[i] = alpha * y.get(i) + (1.0 - alpha) * (St[i - 1] + Bt[i - 1]);
            }
            //Calculate trend smoothing  计算所有的趋势
            Bt[i] = gamma * (St[i] - St[i - 1]) + (1 - gamma) * Bt[i - 1];
            //Calculate seasonal smoothing  计算所有的季节趋势
            if((i - period) >= 0) {
                It[i] = beta * y.get(i) / St[i] + (1.0 - beta) * It[i - period];
            }
            //Calculate forecast  计算预期值
            if( ((i + m) >= period) ){
                Ft[i + m] = (St[i] + (m * Bt[i])) * It[i - period + m];
                if(Ft[i+m]<=0){
                    Ft[i+m]=1;
                }
            }
            if(debug){
                System.out.println(String.format(
                        "i = %d, y = %d, S = %f, Bt = %f, It = %f, F = %f", i,
                        y.get(i), St[i], Bt[i], It[i], Ft[i]));
            }
        }
        return Ft;
    }
    /**
     * See: http://robjhyndman.com/researchtips/hw-initialization/
     * 1st period's average can be taken. But y[0] works better.
     * 
     * @return - Initial Level value i.e. St[1]
     */
    private static double calculateInitialLevel(List<Integer> y, int period) {
        /**     
         double sum = 0;
        for (int i = 0; i < period; i++) {
            sum += y[i];
        }
        return sum / period;
         **/
        return y.get(0);
    }
    /**
     * See: http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc435.htm
     * 
     * @return - Initial trend - Bt[1]
     */
    private static double calculateInitialTrend(List<Integer> y, int period){
        double sum = 0;
        for (int i = 0; i < period; i++) {          
            sum += (y.get(period+i)- y.get(i));
        }
        return sum / (period * period);
    }
    /**
     * See: http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc435.htm
     * 计算及季节性指数
     * @return - Seasonal Indices.
     */
    private static double[] calculateSeasonalIndices(List<Integer> y, int period, int seasons){
        double[] seasonalAverage = new double[seasons];
        double[] seasonalIndices = new double[period];
        double[] averagedObservations = new double[y.size()];
        //获取所有季节各个周期的平均值
        for (int i = 0; i < seasons; i++) {
            for (int j = 0; j < period; j++) {
                seasonalAverage[i] += y.get((i * period) + j);
            }
            seasonalAverage[i] /= period;
        }
        //平均观测值
        for (int i = 0; i < seasons; i++) {
            for (int j = 0; j < period; j++) {
                //真正的值/平均值
                averagedObservations[(i * period) + j] =y.get((i * period) + j) / seasonalAverage[i];               
            }           
        }
        //初始季节性指数
        for (int i = 0; i < period; i++) {
            for (int j = 0; j < seasons; j++) {
                seasonalIndices[i] += averagedObservations[(j * period) + i];
            }           
            seasonalIndices[i] /= seasons;
        }
        return seasonalIndices;
    }
    /**
     * Utility method to pring array values.
     * 
     * @param description
     * @param data
     */
    private static void printArray(String description, double[] data){
        System.out.println(String.format("******************* %s *********************", description));
        for (int i = 0; i < data.length; i++) {
            System.out.println(data[i]);
        }
        System.out.println(String.format("*****************************************************************", description));
    }
    /**
     * 求出二次最佳预测
     * @param y
     * @param m
     * @return
     */
    public static double[] betterSec(List<Integer> y, int m) {
        double[] result = null;
        double avg=0;
        for(int i=0;i<alphas.length;i++){
            int sum=0;
            double[] temp=getSecExpect(y,m,alphas[i]);
            for(int j=0;j<y.size();j++){
                sum+=Math.abs(y.get(j)-temp[j]);
            }
            if(i==0){
                avg=sum/y.size();
                result=temp;
            }
            if(avg<sum/y.size()){
                result=temp;
            }
        }
        return result;
    }
    /**
     * 二次指数平滑法求预测值 预测线性趋势
     * 
     * @param list
     *            基础数据集合
     * @param year
     *            未来第几期
     * @param modulus
     *            平滑系数
     * @return 预测值
     */
    public static double[] getSecExpect(List<Integer> list, int year, Double modulus) {
        if (list.size() < 20 || modulus <= 0 || modulus >= 1) {
            return null;
        }
        double[] ft=new double[list.size()+year];
        Double modulusLeft = 1 - modulus;
        // 一次预测 >20项默认第一个值为初始值
        Double lastIndex = Double.parseDouble(list.get(0).toString());
        // 二次预测 >20项默认第一个值为初始值
        Double lastSecIndex = Double.parseDouble(list.get(0).toString());
        for (int i=0;i<list.size();i++) {
            // st1
            lastIndex = modulus * list.get(i) + modulusLeft * lastIndex;
            // st2
            lastSecIndex = modulus * lastIndex + modulusLeft * lastSecIndex;
            // AT
            Double a = 2 * lastIndex - lastSecIndex;
            // BT
            Double b = (modulus / modulusLeft) * (lastIndex - lastSecIndex);
            ft[i+1]=a+b*year;
            if(ft[i+1]<=0){
                ft[i+1]=1;
            }
        }
        return ft;
    }
    /**
     * 三次指数平滑法求预测值 预测线性趋势
     * 
     * @param list
     *            基础数据集合
     * @param year
     *            未来第几期
     * @param modulus
     *            平滑系数
     * @return 预测值
     */
    public static Double getThridExpect(List<Integer> list, int year, Double modulus) {
        if (list.size() < 20 || modulus <= 0 || modulus >= 1) {
            return null;
        }
        Double modulusLeft = 1 - modulus;
        // 一次预测 >20项默认第一个值为初始值
        Double lastIndex =(double) ((list.get(0)+list.get(1)+list.get(2))/3);
        // 二次预测 >20项默认第一个值为初始值
        Double lastSecIndex = lastIndex;
        // 三次预测 >20项默认第一个为初始值
        Double lastThreadIndes = lastIndex;
        for (Integer data : list) {
            // st1
            lastIndex = modulus * data + modulusLeft * lastIndex;
            // st2
            lastSecIndex = modulus * lastIndex + modulusLeft * lastSecIndex;
            // st3
            lastThreadIndes = modulus * lastSecIndex + modulusLeft * lastThreadIndes;
        }
        // AT
        Double a = 3 * lastIndex - 3 * lastSecIndex + lastThreadIndes;
        // BT
        Double b = (modulus / (2 * modulusLeft * modulusLeft)) * ((6 - 5 * modulus) * lastIndex
                - 2 * (5 - 4 * modulus) * lastSecIndex + (4 - 3 * modulus) * lastThreadIndes);
        // CT
        Double c = ((modulus * modulus) / (2 * modulusLeft *modulusLeft))
                * (lastIndex - 2 * lastSecIndex + lastThreadIndes);
        Double except=a+b*year+c*year*year;
        return except<0?0:except;
    }
}

代码来源CSDN:

作者:WX5991

原文:https://blog.csdn.net/WX5991/article/details/80082041 


附上程序效果:


微信图片_20220421155234.jpg


这是一组预测得比较好的数据。。。


相关文章
|
6天前
|
JavaScript NoSQL Java
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
148 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
|
2月前
|
监控 算法 网络协议
Java 实现局域网电脑屏幕监控算法揭秘
在数字化办公环境中,局域网电脑屏幕监控至关重要。本文介绍用Java实现这一功能的算法,涵盖图像采集、数据传输和监控端显示三个关键环节。通过Java的AWT/Swing库和Robot类抓取屏幕图像,使用Socket进行TCP/IP通信传输图像数据,并利用ImageIO类在监控端展示图像。整个过程确保高效、实时和准确,为提升数字化管理提供了技术基础。
84 15
|
5天前
|
机器学习/深度学习 存储 算法
近端策略优化(PPO)算法的理论基础与PyTorch代码详解
近端策略优化(PPO)是深度强化学习中高效的策略优化方法,广泛应用于大语言模型的RLHF训练。PPO通过引入策略更新约束机制,平衡了更新幅度,提升了训练稳定性。其核心思想是在优势演员-评论家方法的基础上,采用裁剪和非裁剪项组成的替代目标函数,限制策略比率在[1-ϵ, 1+ϵ]区间内,防止过大的策略更新。本文详细探讨了PPO的基本原理、损失函数设计及PyTorch实现流程,提供了完整的代码示例。
108 10
近端策略优化(PPO)算法的理论基础与PyTorch代码详解
|
12天前
|
存储 算法 Java
解锁“分享文件”高效密码:探秘 Java 二叉搜索树算法
在信息爆炸的时代,文件分享至关重要。二叉搜索树(BST)以其高效的查找性能,为文件分享优化提供了新路径。本文聚焦Java环境下BST的应用,介绍其基础结构、实现示例及进阶优化。BST通过有序节点快速定位文件,结合自平衡树、多线程和权限管理,大幅提升文件分享效率与安全性。代码示例展示了文件插入与查找的基本操作,适用于大规模并发场景,确保分享过程流畅高效。掌握BST算法,助力文件分享创新发展。
|
25天前
|
存储 人工智能 算法
解锁分布式文件分享的 Java 一致性哈希算法密码
在数字化时代,文件分享成为信息传播与协同办公的关键环节。本文深入探讨基于Java的一致性哈希算法,该算法通过引入虚拟节点和环形哈希空间,解决了传统哈希算法在分布式存储中的“哈希雪崩”问题,确保文件分配稳定高效。文章还展示了Java实现代码,并展望了其在未来文件分享技术中的应用前景,如结合AI优化节点布局和区块链增强数据安全。
|
26天前
|
算法 安全 Java
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
66 16
|
1月前
|
运维 监控 算法
企业局域网监控软件中 Java 优先队列算法的核心优势
企业局域网监控软件是数字化时代企业网络安全与高效运营的基石,犹如一位洞察秋毫的卫士。通过Java实现的优先队列算法,它能依据事件优先级排序,确保关键网络事件如异常流量、数据泄露等被优先处理,保障系统稳定与安全。代码示例展示了如何定义网络事件类并使用PriorityQueue处理高优先级事件,尤其在面对疑似风险时迅速启动应急措施。这一核心技术助力企业在复杂网络环境中稳健前行,护航业务腾飞。
65 32
|
1月前
|
存储 监控 算法
剖析基于Java算法驱动的智能局域网管控之道
本文探讨了基于Java语言的局域网控制方案,结合链表数据结构与令牌桶算法,解决设备管理和流量调度难题。通过链表灵活存储网络设备信息,实现高效设备管理;令牌桶算法则精准控制流量,确保网络平稳运行。二者相辅相成,为校园、企业等局域网提供稳固高效的控制体系,保障业务连续性和数据安全。
|
29天前
|
算法 搜索推荐 Java
【潜意识Java】深度解析黑马项目《苍穹外卖》与蓝桥杯算法的结合问题
本文探讨了如何将算法学习与实际项目相结合,以提升编程竞赛中的解题能力。通过《苍穹外卖》项目,介绍了订单配送路径规划(基于动态规划解决旅行商问题)和商品推荐系统(基于贪心算法)。这些实例不仅展示了算法在实际业务中的应用,还帮助读者更好地准备蓝桥杯等编程竞赛。结合具体代码实现和解析,文章详细说明了如何运用算法优化项目功能,提高解决问题的能力。
61 6
|
29天前
|
算法 Java C++
【潜意识Java】蓝桥杯算法有关的动态规划求解背包问题
本文介绍了经典的0/1背包问题及其动态规划解法。
48 5

热门文章

最新文章