预测算法 | 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


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


相关文章
|
1月前
|
JavaScript NoSQL Java
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
202 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
|
2月前
|
存储 算法 安全
探究‘公司禁用 U 盘’背后的哈希表算法与 Java 实现
在数字化办公时代,信息安全至关重要。许多公司采取“禁用U盘”策略,利用哈希表算法高效管理外接设备的接入权限。哈希表通过哈希函数将设备标识映射到数组索引,快速判断U盘是否授权。例如,公司预先将允许的U盘标识存入哈希表,新设备接入时迅速验证,未授权则禁止传输并报警。这有效防止恶意软件和数据泄露,保障企业信息安全。 代码示例展示了如何用Java实现简单的哈希表,模拟公司U盘管控场景。哈希表不仅用于设备管理,还在文件索引、用户权限等多方面助力信息安全防线的构建,为企业数字化进程保驾护航。
|
14天前
|
消息中间件 Java 应用服务中间件
JVM实战—1.Java代码的运行原理
本文介绍了Java代码的运行机制、JVM类加载机制、JVM内存区域及其作用、垃圾回收机制,并汇总了一些常见问题。
JVM实战—1.Java代码的运行原理
|
12天前
|
JavaScript 前端开发 算法
JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能,JS中排序算法的使用详解(附实际应用代码)
Array.sort() 是一个功能强大的方法,通过自定义的比较函数,可以处理各种复杂的排序逻辑。无论是简单的数字排序,还是多字段、嵌套对象、分组排序等高级应用,Array.sort() 都能胜任。同时,通过性能优化技巧(如映射排序)和结合其他数组方法(如 reduce),Array.sort() 可以用来实现高效的数据处理逻辑。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
1月前
|
机器学习/深度学习 存储 算法
近端策略优化(PPO)算法的理论基础与PyTorch代码详解
近端策略优化(PPO)是深度强化学习中高效的策略优化方法,广泛应用于大语言模型的RLHF训练。PPO通过引入策略更新约束机制,平衡了更新幅度,提升了训练稳定性。其核心思想是在优势演员-评论家方法的基础上,采用裁剪和非裁剪项组成的替代目标函数,限制策略比率在[1-ϵ, 1+ϵ]区间内,防止过大的策略更新。本文详细探讨了PPO的基本原理、损失函数设计及PyTorch实现流程,提供了完整的代码示例。
372 10
近端策略优化(PPO)算法的理论基础与PyTorch代码详解
|
22天前
|
传感器 监控 Java
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
45 5
|
1月前
|
存储 算法 Java
解锁“分享文件”高效密码:探秘 Java 二叉搜索树算法
在信息爆炸的时代,文件分享至关重要。二叉搜索树(BST)以其高效的查找性能,为文件分享优化提供了新路径。本文聚焦Java环境下BST的应用,介绍其基础结构、实现示例及进阶优化。BST通过有序节点快速定位文件,结合自平衡树、多线程和权限管理,大幅提升文件分享效率与安全性。代码示例展示了文件插入与查找的基本操作,适用于大规模并发场景,确保分享过程流畅高效。掌握BST算法,助力文件分享创新发展。
|
2月前
|
存储 人工智能 算法
解锁分布式文件分享的 Java 一致性哈希算法密码
在数字化时代,文件分享成为信息传播与协同办公的关键环节。本文深入探讨基于Java的一致性哈希算法,该算法通过引入虚拟节点和环形哈希空间,解决了传统哈希算法在分布式存储中的“哈希雪崩”问题,确保文件分配稳定高效。文章还展示了Java实现代码,并展望了其在未来文件分享技术中的应用前景,如结合AI优化节点布局和区块链增强数据安全。
|
2月前
|
算法 安全 Java
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
111 16
|
2月前
|
存储 监控 算法
剖析基于Java算法驱动的智能局域网管控之道
本文探讨了基于Java语言的局域网控制方案,结合链表数据结构与令牌桶算法,解决设备管理和流量调度难题。通过链表灵活存储网络设备信息,实现高效设备管理;令牌桶算法则精准控制流量,确保网络平稳运行。二者相辅相成,为校园、企业等局域网提供稳固高效的控制体系,保障业务连续性和数据安全。