应用愿景:目前很多ERP系统虽然可利用的数据种类繁多,数据量大,但是对于预测部分的开发不够。本文抛砖引玉,希望能够引起更多的重视。

在商业活动中,对于行业未来发展的预测应该是各大企业集团的重要任务。预测不光有技术预测,如二十年代末期,MR硬盘磁头被GMR巨磁阻磁头逐步取代时,一位硬盘技术专家说随着磁记录点不断变小,单碟容量的增加还要几十年才能到达极限,那时他已经可以退休了。除此之外对市场状况的预测也备受关注,其中对某种新产品在未来一段时间的销量也是研究的重点。

常用的预测方法根据预测方法的特征,可分为以下几种:

(1) 定性预测:这是一种依靠人的主观判断预测未来的方法。

(2) 因果关系预测:经济变量之间往往存在着某种因果关系。找出影响某种结果(因变量)的一个或几个因素(自变量),建立数学模型,然后根据自变量的变化,预测因变量的变化,这就是因果关系预测的基本方法。

(3) 时间序列预测:按照时间顺序排列的一组数据,称为时间序列。根据时间序列所体现出来的某种事件的发展趋势,可预测该事件的未来。

本软件ForecastSoft利用产品生命周期内产品销量与客流量的关系,利用二项分布当数据量大时近似于正态分布的原理进行预测。主要方法是统计一段时间内所有店铺的总客流量以及一种或多种相似产品的总销量,来预测产品销量。本软件的最大可能性法的结果实际上就是该种产品的销量或多种产品的平均值。就客流量统计方面,该软件可能比较适合网店销售预测。

软件主要分为两部分:

1)数据管理部分:管理用户,店铺,产品,统计周期,店铺客流量以及产品销量等数据。建立User,Store,Product,Period等对象类,用MySQL数据库存储以上数据。本部分比较简单不做说明。

2)产品销量预测部分:期望值标准、最大可能性标准和合理性标准,其中应用的最多的是最大可能性标准。

(1)首先采用期望值标准作为决策标准,求得产品销量a;

(2)用最大可能性作为决策标准,求得产品销量b;

(3)用最大可能全部售出作为决策标准,即售罄概率大于95%时,求得产品销量c;

由于该预测原本用于预测生命周期短的时装产品,所以用于预测计算的类命名为ForecastFashion.java,再加上用于计算利润的Profit.Java类构成了预测部分。预测结果用ResultDisplayPanel.java类显示。


package com.mr.forecast.service;

import org.apache.commons.statistics.distribution.NormalDistribution;

import com.mr.forecast.pojo.Period;
import com.mr.forecast.service.ForecastService;
import com.mr.forecast.session.Session;

/**
 * 计算模块,运用预测方法计算出结果
 * 
 * @author harry
 *
 */

public class ForecastFashion {

	public double p; // 产品购买概率
	public double mean; // 分布曲线的均值
	public double sigma; // 分布曲线的均方根
	public int a; // 期望值法(最大利润)预测销售数量
	public int b; // 最大可能性法预测销售数量
	public int c; // 售罄法(可能性 c_Probability < 5%)计算预测销售数量	
	public int cp; // 售罄法的可能性 c_Probability = 5%
	double sumflow; // /获取总客流量
	String[][] sumsalesbyprod; //单类产品销量数组{prodcode, sales(sum)}
	double sumsales; // 所有产品总销量

	public ForecastFashion() {
	}

	public void forecasting() { // 预测方法forecasting
		Period period = Session.statperiod;// 获取当前统计日期区间
		sumflow = ForecastService.getSumFlow(period); //获取总客流量
		sumsalesbyprod = ForecastService.getSumSalesByProd(period); //获取所有产品总销量
		for (int i = 0; i< sumsalesbyprod.length; i++) {// 计算所有产品总销量
			sumsales += Double.valueOf(sumsalesbyprod[i][1]);// 单个产品销量累加
		}
//		System.out.println(sumsales);	
		double sales = sumsales/sumsalesbyprod.length; //计算每个产品销量的平均值
		double scf = sumflow;
//		System.out.println("sumsalesbyprod.length=" + sumsalesbyprod.length +"sales=" + sales +"sumflow=" + sumflow);
		// 计算 销售概率 p 值
		double p = sales / scf;

		// 计算 mean 和 sigma 值
		double mean = scf * p;
		double sigma = Math.sqrt(scf * p * (1 - p));

//		System.out.println("概率 p = " + p + "平均值 = " + mean);

		// 构建均值为 mean 均方差为 sigma 的正态分布对象
		NormalDistribution normalDistribution = NormalDistribution.of(mean, sigma);

		// 采用a.期望值法(最大利润)计算预测销售数量
		
//		int step =(int) (6*sigma/20);  
		int step = 1;                   //假定预测和生产产品数量的步距,计算在该步距区间出现的概率		
//		System.out.println("step=" + step);  
		// 设定预测和生产产品数量的取值范围 mean+/-6*sigma
		int x = (int)mean -(int) (6*sigma);    
		int x0 = x;                            // 区间下限坐标x0
		int x1 = x + step;                     // 区间下限坐标x1
		int numofstep=(int) (6*sigma*2);       // 设定预测和生产产品数量的取值范围内,区间的数量
//		int numofstep=40;		
		double cdf[] = new double[numofstep]; //定义数组存放在该步距区间出现的概率
		
//		System.out.println("x0=" + x0 + "   x1 = " + x1 + "   numofstep = " + numofstep);		
		
//		double x = mean - 200;    // 求区间下限
//		double x0 = x;
//		double x1 = x + 10;
//		double cdf[] = new double[40];

		for (int i = 0; i < numofstep; ++i) {
			cdf[i] = normalDistribution.probability(x0, x1);     //求在该步距区间出现的概率并存放数组
			// System.out.println("概率 cdf[" + i +"] = "+ cdf[i]);
			x0 += step;
			x1 += step;
//			x0 += 10;  // 间距
//			x1 += 10;		
			// System.out.println("x0=" + x0 +"x1 = "+ x1);
		}
//		System.out.println("x0=" + x0 + "x1 = " + x1);

		double xProduced = (int)mean -(int) (6*sigma)+ step;   // 确定最小生产数量
//		double xProduced = mean - 20*step + 0.5*step;   // 确定最小生产数量
		double xExtimated = xProduced;                  //假设市场需求数量最小值
		Profit prodProfit = new Profit();   // 创建利润类
		// 计算不同生产数量时的利润,并存入数组 
		double profitExp[] = new double[numofstep];  //定义数组存放选定某一生产数量后的利润		
		for (int i = 0; i < numofstep; ++i) {   //给profitExp[]数组赋初值
			profitExp[i] = 0;
		}
		for (int i = 0; i < numofstep; i++) {
			xExtimated = (int)mean -(int) (6*sigma)+ step; 
//			xExtimated = mean - 20*step + 0.5*step;
			for (int j = 0; j < numofstep; ++j) {
				profitExp[i] += prodProfit.calculateProfit(xProduced, xExtimated) * cdf[j];
				xExtimated += step;
			}
			xProduced += step;
//			System.out.println("profitExp[" + i + "] = " + profitExp[i]);
		}
		//求最大利润,利用相应的下标n求对应的生产数量a
		int n = 0;
		for (int i = 0; i < numofstep - 1; ++i) {
			if (profitExp[i] < profitExp[i + 1]) {
				n = i + 1; 
			}
		}
		int a = (int) (((int)mean -(int) (6*sigma)+ step) + n*step);
//		int a = (int) ((mean - 20*step + 0.5*step) + step * n);
//		System.out.println("profitExp[" + n + "] = " + profitExp[n]);
//		int a = (int) ((mean - 200 + 5) + 10 * 21);
//		System.out.println("期望值法(最大利润)计算预测销售数量a = " + a);  

		// 采用b.最大可能性法计算预测销售数量
		int b = (int) ForecastService.round(mean, 0);
//		System.out.println("最大可能性法计算预测销售数量b = " + ForecastService.round(mean, 0)); 
		
		// 采用c.售罄法(可能性 c_Probability = 5%)计算预测销售数量
		double c_Probability = 0.05;
		double productQty_c = normalDistribution.inverseCumulativeProbability(c_Probability);
		// 利用反正态分布函数求得 可能性 c_Probability = 5%)的预测销售数量
		int cp = (int) (c_Probability * 100); // c_Probability转换为int类型数据 cp
		int c = (int) productQty_c; // productQty_c 转换为int类型数据 c

		setA(a);
		setB(b);
		setCp(cp);
		setC(c);
		setP(p);
		setMean(mean);
		setSigma(sigma);
	}

	public double getP() {
		return p;
	}

	public void setP(double p) {
		this.p = p;
	}

	public double getMean() {
		return mean;
	}

	public void setMean(double mean) {
		this.mean = mean;
	}

	public double getSigma() {
		return sigma;
	}

	public void setSigma(double sigma) {
		this.sigma = sigma;
	}

	public int getA() {
		return a;
	}

	public void setA(int a) {
		this.a = a;
	}

	public int getB() {
		return b;
	}

	public void setB(int b) {
		this.b = b;
	}

	public int getCp() {
		return cp;
	}

	public void setCp(int cp) {
		this.cp = cp;
	}

	public int getC() {
		return c;
	}

	public void setC(int c) {
		this.c = c;
	}

}

ForecastFashion.java类中引入了NormalDistribution类包,简化软件编程。

import org.apache.commons.statistics.distribution.NormalDistribution;
package com.mr.forecast.service;

import com.mr.forecast.pojo.Product;
import com.mr.forecast.session.Session;

/**
 * 计算市场需求与生产量不同时的利润
 * 
 * @author harry
 *
 */
public class Profit {

	// String costItem[] = { "价格", "固定成本", "可变成本", "残值" };
	// public double cost[] = {800,0,300,50}; //定义成本相关参数:价格,固定成本, 可变成本, 残值
	private double cost[]; // 定义产品成本数组
	public double profit = 0.0;// 定义利润 profit

	/** 定义构造函数 */
	public Profit() {
		super();
	}

	/** 定义计算利润p方法, 已知生产数量x和预测值xEst。 */
	public double calculateProfit(double x, double xEst) {

		// 创建产品集合的迭代器
		for (Product e : Session.PROD_SET) {// 遍历所有产品
			if (e.getProdCode().equals(Session.prodcost)) {// 如果编号是一样的
				cost = new double[4];
				cost[0] = e.getPrice();// 返回单个产品价格
				cost[1] = e.getFixCost();// 返回单个产品固定成本
				cost[2] = e.getVarCost();// 返回单个产品可变成本
				cost[3] = e.getValue();// 返回单个产品滞销产品的剩余价值
			}
		}
//		 System.out.println(Session.prodcost+ "价格, 固定成本 , 可变成本 , 残值"+ cost[0] + cost[1] + cost[2] + cost[3]);

		/** 计算利润, 生产数量<=预测值。 */
		if (Double.compare(x, xEst) <= 0) {
			// profit = 800*x-(0+300*x);
			profit = cost[0] * x - (cost[1] + cost[2] * x);
			return profit;
		}
		// profit = 800*xEst-(0+300*x)+(x-xEst)*50;
		profit = cost[0] * xEst - (cost[1] + cost[2] * x) + (x - xEst) * cost[3];
		return profit;
	}
}


主界面:

ForecastSoft一种运用正态分布预测产品销量的简单软件_产品销量预测

客流量界面:

ForecastSoft一种运用正态分布预测产品销量的简单软件_产品销量预测_02

销量界面:

ForecastSoft一种运用正态分布预测产品销量的简单软件_预测软件_03

结果显示界面:

ForecastSoft一种运用正态分布预测产品销量的简单软件_产品销量预测_04


软件名称: ForecastSoft

操作系统: Window10 64位。

JDK版本: OpenJDK 11。

开发工具: Eclipse 2019-12。

数据库: MySQL 8.0。


主要源代码程序已经在上文公开。本软件仅供学习交流使用,如有需要可联系本人获取。


参考文献:

1)本文借用了《Java从入门到精通》明日科技编著的部分代码。

2)预测原理参考了《时尚女装加工配送的供应链管理研究》作者:章卫  上海交通大学学位论文