ARIMA模型原理及其java實作
一.基本原理
ARIMA模型是通過将預測對象随時間推移而形成的資料序列當成一個随機序列,進而用一定的數學模型來近似表述該序列。根據原序列是否平穩以及回歸中所包含部分的不同分為AR、MA、ARMA以及ARIMA過程。
在模型的使用過程中需要根據時間序列的自相關函數、偏自相關函數等對序列的平穩性進行判别;而對于非平穩序列一般都需要通過差分處理将其轉換成平穩序列(ARIMA);對得到的平穩序列進行模組化以确定最佳模型(AR、MA、ARMA或者ARIMA)。在使用中最重要也是最關鍵的就是對序列進行參數估計,以檢驗其是否具有統計意義。具體的理論分析見相關函數及偏相關函數。
二. 模型參數估計
AR、MA以及ARMA過程的參數估計方法主要包含Yule-Walker估計、最小二乘估計、最大似然估計以及逆相關函數法、資訊估計方法等,具體的推導過程以及證明參見時間序列 ARMA模型的參數估計。
三. Java實作
在Java實作中,主要給出了AR、MA以及ARMA的參數估計方法,并未對其平穩性以及模型的最佳階數進行嚴格性證明,隻是通過周遊模型參數清單的方式由AIC準則或者BIC準則确定最佳p、q階數。同時在參數估計的過程中,主要是利用Yule-Walker方法進行求解;同時為了避免在求解過程中進行逆矩陣的計算,采用Levinson遞推公式求解Y-W方程,得到模型的參數。詳細的理論推導見前一連結。
1). Y-W方程求解
/**
* @param garma 代表的是資料的協方差
* @return 傳回經由Y-W方程求解的結果,其中數組的最後
* 一個元素存儲的是模型中的噪聲方差
*/
public double [] YWSolve(double [] garma)
{
int order = garma.length - ;
double [] garmaPart = new double[order];
System.arraycopy(garma, , garmaPart, , order);
// 将協方差轉換為矩陣的形式
double [][] garmaArray = new double[order][order];
for (int i = ; i < order; ++i)
{
// 對角線
garmaArray[i][i] = garma[];
//下三角
int subIndex = i;
for (int j = ; j < i; ++j)
{
garmaArray[i][j] = garma[subIndex--];
}
//上三角
int topIndex = i;
for (int j = i + ; j < order; ++j)
{
garmaArray[i][j] = garma[++topIndex];
}
}
/* 調用了juma包,其實作了大部分對矩陣的操作 */
/* 可能會存在矩陣不可逆的情況,在矩陣不可逆時
可以通過将對角線元素全部增加1e-6做修正 */
Matrix garmaMatrix = new Matrix(garmaArray);
Matrix garmaMatrixInverse = garmaMatrix.inverse();
Matrix autoReg = garmaMatrixInverse.times(new Matrix(garmaPart, order));
double [] result = new double[autoReg.getRowDimension() + ];
for (int i = ; i < autoReg.getRowDimension(); ++i)
{
result[i] = autoReg.get(i, );
}
double sum = ;
for (int i = ; i < order; ++i)
{
sum += result[i] * garma[i];
}
result[result.length - ] = garma[] - sum;
return result;
}
2). Levinson 遞推公式求解
/**
* @param garma 代表的是資料的協方差
* @return 傳回結果的第一行元素代表的是在疊代過程中的方差,
* 其餘的元素代表的是疊代過程中存儲的系數
*/
public double [][] LevinsonSolve(double [] garma)
{
int order = garma.length - ;
double [][] result = new double[order + ][order + ];
double [] sigmaSq = new double[order + ];
sigmaSq[0] = garma[];
result[][] = garma[] / sigmaSq[0];
sigmaSq[1] = sigmaSq[0] * ( - result[][] * result[][]);
for (int k = ; k < order; ++k)
{
double sumTop = .;
double sumSub = .;
for (int j = ; j <= k; ++j)
{
sumTop += garma[k + - j] * result[k][j];
sumSub += garma[j] * result[k][j];
}
result[k + ][k + ] = (garma[k + ] - sumTop) / (garma[] - sumSub);
for (int j = ; j <= k; ++j)
{
result[k + ][j] = result[k][j] - result[k + ][k + ] * result[k][k + - j];
}
sigmaSq[k + 1] = sigmaSq[k] * ( - result[k + ][k + ] * result[k + ][k + ]);
}
result[] = sigmaSq;
return result;
}
3). AR(p) 參數估計
/**
* @param originalData 原始資料
* @param p 模型的階數
* @return AR模型的系數
*/
public double [] computeARCoe(double [] originalData, int p)
{
double [] garma = this.autoCovData(originalData, p); //p+1
double [][] result = this.LevinsonSolve(garma); //(p + 1) * (p + 1)
double [] ARCoe = new double[p + ];
for (int i = ; i < p; ++i)
{
ARCoe[i] = result[p][i + ];
}
ARCoe[p] = result[][p]; //噪聲參數
// return this.YWSolve(garma);
return ARCoe;
}
4) MA(q) 參數估計
在求解MA系數的過程中,需要先求解AR(pn)模型。通過确定的pn階數來求取MA(q)模型的偏相關函數,進而求得MA(q)的系數。當然此處的AR(pn)的階數也可自己給定,比如K*log(N),K為一整數,N為資料長度,具體可參見前一連結。
/**
* @param originalData 原始資料
* @param q 模型階數
* @return MA系數
*/
public double [] computeMACoe(double [] originalData, int q)
{
// 确定最佳的p
int p = ;
double minAIC = Double.MAX_VALUE;
int len = originalData.length;
if (len > )
{
len = ;
}
for (int i = ; i < len; ++i)
{
double [] garma = this.autoCovData(originalData, i);
double [][] result = this.LevinsonSolve(garma);
double [] ARCoe = new double[i + ];
for (int k = ; k < i; ++k)
{
ARCoe[k] = result[i][k + ];
}
ARCoe[i] = result[][i];
// double [] ARCoe = this.YWSolve(garma);
Vector<double []> vec = new Vector<>();
vec.add(ARCoe);
double aic = this.getModelAIC(vec, originalData, );
if (aic < minAIC)
{
minAIC = aic;
p = i;
}
}
// System.out.println("The best p is " + p);
// 求取系數
double [] bestGarma = this.autoCovData(originalData, p);
double [][] bestResult = this.LevinsonSolve(bestGarma);
double [] alpha = new double[p + ];
alpha[] = -;
for (int i = ; i <= p; ++i)
{
alpha[i] = bestResult[p][i];
}
// double [] result = this.YWSolve(bestGarma);
// double [] alpha = new double[p + 1];
// alpha[0] = -1;
// for (int i = 1; i <= p; ++i)
// {
// alpha[i] = result[i - 1];
// }
double [] paraGarma = new double[q + ];
for (int k = ; k <= q; ++k)
{
double sum = ;
for (int j = ; j <= p - k; ++j)
{
sum += alpha[j] * alpha[k + j];
}
paraGarma[k] = sum / bestResult[][p];
}
double [][] tmp = this.LevinsonSolve(paraGarma);
double [] MACoe = new double[q + ];
for (int i = ; i < MACoe.length; ++i)
{
MACoe[i] = tmp[q][i];
}
MACoe[] = tmp[][q]; //噪聲參數
// double [] tmp = this.YWSolve(paraGarma);
// double [] MACoe = new double[q + 1];
// System.arraycopy(tmp, 0, MACoe, 1, tmp.length - 1);
// MACoe[0] = tmp[tmp.length - 1];
return MACoe;
}
5) ARMA(p, q) 參數估計
/**
* @param originalData 原始資料
* @param p AR模型階數
* @param q MA模型階數
* @return ARMA模型系數
*/
public double [] computeARMACoe(double [] originalData, int p, int q)
{
double [] allGarma = this.autoCovData(originalData, p + q);
double [] garma = new double[p + ];
for (int i = ; i < garma.length; ++i)
{
garma[i] = allGarma[q + i];
}
double [][] arResult = this.LevinsonSolve(garma);
// AR
double [] ARCoe = new double[p + ];
for (int i = ; i < p; ++i)
{
ARCoe[i] = arResult[p][i + ];
}
ARCoe[p] = arResult[][p];
// double [] ARCoe = this.YWSolve(garma);
// MA
double [] alpha = new double[p + ];
alpha[] = -;
for (int i = ; i <= p; ++i)
{
alpha[i] = ARCoe[i - ];
}
double [] paraGarma = new double[q + ];
for (int k = ; k <= q; ++k)
{
double sum = ;
for (int i = ; i <= p; ++i)
{
for (int j = ; j <= p; ++j)
{
sum += alpha[i] * alpha[j] * allGarma[Math.abs(k + i - j)];
}
}
paraGarma[k] = sum;
}
double [][] maResult = this.LevinsonSolve(paraGarma);
double [] MACoe = new double[q + ];
for (int i = ; i <= q; ++i)
{
MACoe[i] = maResult[q][i];
}
MACoe[] = maResult[][q];
// double [] tmp = this.YWSolve(paraGarma);
// double [] MACoe = new double[q + 1];
// System.arraycopy(tmp, 0, MACoe, 1, tmp.length - 1);
// MACoe[0] = tmp[tmp.length - 1];
double [] ARMACoe = new double[p + q + ];
for (int i = ; i < ARMACoe.length; ++i)
{
if (i < ARCoe.length)
{
ARMACoe[i] = ARCoe[i];
}
else
{
ARMACoe[i] = MACoe[i - ARCoe.length];
}
}
return ARMACoe;
}
6) 求解過程中運用到的相關函數
/**
* @param originalData
* @return 均值
*/
public double avgData(double [] originalData)
{
return this.sumData(originalData) / originalData.length;
}
/**
* @param originalData
* @return 求和
*/
public double sumData(double [] originalData)
{
double sum = ;
for (int i = ; i < originalData.length; ++i)
{
sum += originalData[i];
}
return sum;
}
/**
* 計算标準差 sigma = sqrt(var);
* @param originalData
* @return 标準差
*/
public double stdErrData(double [] originalData)
{
return Math.sqrt(this.varErrData(originalData));
}
/**
* 計算方差 var = sum(x - mu) ^2 / N;
* @param originalData
* @return 方差
*/
public double varErrData(double [] originalData)
{
if (originalData.length <= )
return ;
double var = ;
double mu = this.avgData(originalData);
for (int i = ; i < originalData.length; ++i)
{
var += (originalData[i] - mu) * (originalData[i] - mu);
}
var /= (originalData.length - ); //方差的無偏估計
return var;
}
/**
* @param dataFir
* @param dataSec
* @return 皮爾遜相關系數(互相關)
*/
public double mutalCorr(double [] dataFir, double [] dataSec)
{
double sumX = ;
double sumY = ;
double sumXY = ;
double sumXSq = ;
double sumYSq = ;
int len = ;
if (dataFir.length != dataSec.length)
{
len = Math.min(dataFir.length, dataSec.length);
}
else
{
len = dataFir.length;
}
for (int i = ; i < len; ++i)
{
sumX += dataFir[i];
sumY += dataSec[i];
sumXY += dataFir[i] * dataSec[i];
sumXSq += dataFir[i] * dataFir[i];
sumYSq += dataSec[i] * dataSec[i];
}
double numerator = sumXY - sumX * sumY / len;
double denominator = Math.sqrt((sumXSq - sumX * sumX / len) * (sumYSq - sumY * sumY / len));
if (denominator == )
{
return ;
}
return numerator/ denominator;
}
/**
* @param data
* @return 互相關矩陣
*/
public double [][] computeMutalCorrMatrix(double [][] data)
{
double [][] result = new double[data.length][data.length];
for (int i = ; i < data.length; ++i)
{
for (int j = ; j < data.length; ++j)
{
result[i][j] = this.mutalCorr(data[i], data[j]);
}
}
return result;
}
/**
* 計算自協方差,C(k)=sum((x(t)-mu)*(x(t-k)-mu))/(N-k);
* @param originalData
* @param order
* @return 自協方差(gama(k))-->認為是自相關系數
*/
public double [] autoCovData(double [] originalData, int order)
{
double mu = this.avgData(originalData);
double [] autoCov = new double[order + ];
for (int i = ; i <= order; ++i)
{
autoCov[i] = ;
for (int j = ; j < originalData.length - i; ++j)
{
autoCov[i] += (originalData[i + j] - mu) * (originalData[j] - mu);
}
autoCov[i] /= (originalData.length - );
}
return autoCov;
}
/**
* 計算自相關函數(系數) rou(k) = C(k) / C(0);
* 其中 C(k) = sum((x(t) - mu)*(x(t - k) - mu)) / (N - k),
* C(0) = var = sum(x(t) - mu) ^2 / N;
* @param originalData
* @param order
* @return 自相關函數(rou(k))
*/
public double [] autoCorrData(double [] originalData, int order)
{
double [] autoCov = this.autoCovData(originalData, order);
double [] autoCorr = new double[order + ]; //預設初始化為0
double var = this.varErrData(originalData);
if (var != )
{
for (int i = ; i < autoCorr.length; ++i)
{
autoCorr[i] = autoCov[i] / var;
}
}
return autoCorr;
}
7) AIC準則
注:本文中關于AIC準則的計算參考自瀚海小松
/**
* @param vec 模型的系數
* @param data 資料
* @param type 標明的模型
* @return
*/
public double getModelAIC(Vector<double []>vec, double [] data, int type)
{
int n = data.length;
int p = , q = ;
double tmpAR = , tmpMA = ;
double sumErr = ;
Random random = new Random();
/* MA */
if (type == )
{
double [] maCoe = vec.get();
q = maCoe.length;
double [] errData = new double[q];
for (int i = q - ; i < n; ++i)
{
tmpMA = ;
for (int j = ; j < q; ++j)
{
tmpMA += maCoe[j] * errData[j];
}
for (int j = q - ; j > ; --j)
{
errData[j] = errData[j - ];
}
errData[] = random.nextGaussian() * Math.sqrt(maCoe[]);
sumErr += (data[i] - tmpMA) * (data[i] - tmpMA);
}
// return Math.log(sumErr) + (q + 1) * 2 / n;
return (n - (q - )) * Math.log(sumErr / (n - (q - ))) + (q + ) * ;
// return (n-(q-1))*Math.log(sumErr/(n-(q-1)))+(q)*Math.log(n-(q-1)); //AIC 最小二乘估計
}
/* AR */
else if (type == )
{
double [] arCoe = vec.get();
p = arCoe.length;
for (int i = p - ; i < n; ++i)
{
tmpAR = ;
for (int j = ; j < p - ; ++j)
{
tmpAR += arCoe[j] * data[i - j - ];
}
sumErr += (data[i] - tmpAR) * (data[i] - tmpAR);
}
// return Math.log(sumErr) + (p + 1) * 2 / n;
return (n - (p - )) * Math.log(sumErr / (n - (p - ))) + (p + ) * ;
// return (n-(p-1))*Math.log(sumErr/(n-(p-1)))+(p)*Math.log(n-(p-1)); //AIC 最小二乘估計
}
/* ARMA */
else
{
double [] arCoe = vec.get();
double [] maCoe = vec.get();
p = arCoe.length;
q = maCoe.length;
double [] errData = new double[q];
for (int i = p - ; i < n; ++i)
{
tmpAR = ;
for (int j = ; j < p - ; ++j)
{
tmpAR += arCoe[j] * data[i - j - ];
}
tmpMA = ;
for (int j = ; j < q; ++j)
{
tmpMA += maCoe[j] * errData[j];
}
for (int j = q - ; j > ; --j)
{
errData[j] = errData[j - ];
}
errData[] = random.nextGaussian() * Math.sqrt(maCoe[]);
sumErr += (data[i] - tmpAR - tmpMA) * (data[i] - tmpAR - tmpMA);
}
// return Math.log(sumErr) + (q + p + 1) * 2 / n;
return (n - (q + p - )) * Math.log(sumErr / (n - (q + p - ))) + (p + q) * ;
// return (n-(p-1))*Math.log(sumErr/(n-(p-1)))+(p+q-1)*Math.log(n-(p-1)); //AIC 最小二乘估計
}
}
8) 相關模型的主類
a) AR 類
package arima;
import java.util.Vector;
public class ARModel
{
private double [] data;
private int p;
public ARModel(double [] data, int p)
{
this.data = data;
this.p = p;
}
public Vector<double []> solveCoeOfAR()
{
Vector<double []>vec = new Vector<>();
double [] arCoe = new ARMAMethod().computeARCoe(this.data, this.p);
vec.add(arCoe);
return vec;
}
}
b) MA 類
package arima;
import java.util.Vector;
public class MAModel
{
private double [] data;
private int q;
public MAModel(double [] data, int q)
{
this.data = data;
this.q = q;
}
public Vector<double []> solveCoeOfMA()
{
Vector<double []>vec = new Vector<>();
double [] maCoe = new ARMAMethod().computeMACoe(this.data, this.q);
vec.add(maCoe);
return vec;
}
}
c) ARMA 類
package arima;
import java.util.Vector;
public class ARMAModel
{
private double [] data = {};
private int p; //AR階數
private int q; //MA階數
public ARMAModel(double [] data, int p, int q)
{
this.data = data;
this.p = p;
this.q = q;
}
/**
* 在ARMA模型中,首先根據原始資料求得AR模型的自回歸系數(AR系數)
* 利用AR系數與原始資料,求解的殘差序列,根據殘差序列的自協方差
* 最終求得ARMA中MA系數
* @return ar, ma
*/
public Vector<double []> solveCoeOfARMA()
{
Vector<double []>vec = new Vector<>();
//ARMA模型
double [] armaCoe = new ARMAMethod().computeARMACoe(this.data, this.p, this.q);
//AR系數
double [] arCoe = new double[this.p + ];
System.arraycopy(armaCoe, , arCoe, , arCoe.length);
//MA系數
double [] maCoe = new double[this.q + ];
System.arraycopy(armaCoe, (this.p + ), maCoe, , maCoe.length);
vec.add(arCoe);
vec.add(maCoe);
return vec;
}
}
d) ARIMA 類
package arima;
import java.util.ArrayList;
import java.util.Random;
import java.util.Vector;
public class ARIMAModel
{
double [] originalData = {};
double [] dataFirDiff = {};
Vector<double []>arimaCoe = new Vector<>();
public ARIMAModel()
{
}
public ARIMAModel(double [] originalData)
{
this.originalData = originalData;
}
public double [] preFirDiff(double [] preData) //一階差分(1)
{
double [] tmpData = new double[preData.length - ];
for (int i = ; i < preData.length - ; ++i)
{
tmpData[i] = preData[i + ] - preData[i];
}
return tmpData;
}
public double [] preSeasonDiff(double [] preData) //季節性差分(6, 7)
{
double [] tmpData = new double[preData.length - ];
for (int i = ; i < preData.length - ; ++i)
{
tmpData[i] = preData[i + ] - preData[i];
}
return tmpData;
}
public double [] preDealDiff(int period)
{
if (period >= originalData.length - ) // 将6也歸為季節性差分
{
period = ;
}
switch (period)
{
case :
return this.originalData;
case :
this.dataFirDiff = this.preFirDiff(this.originalData);
return this.dataFirDiff;
default:
return preSeasonDiff(originalData);
}
}
public int [] getARIMAModel(int period, ArrayList<int []>notModel, boolean needNot)
{
double [] data = this.preDealDiff(period);
double minAIC = Double.MAX_VALUE;
int [] bestModel = new int[];
int type = ;
Vector<double []>coe = new Vector<>();
// model産生, 即産生相應的p, q參數
int len = data.length;
if (len > )
{
len = ;
}
int size = ((len + ) * (len + )) / - ;
int [][] model = new int[size][];
int cnt = ;
for (int i = ; i <= len; ++i)
{
for (int j = ; j <= len - i; ++j)
{
if (i == && j == )
continue;
model[cnt][] = i;
model[cnt++][] = j;
}
}
for (int i = ; i < model.length; ++i)
{
// 控制選擇的參數
boolean token = false;
if (needNot)
{
for (int k = ; k < notModel.size(); ++k)
{
if (model[i][] == notModel.get(k)[] && model[i][] == notModel.get(k)[])
{
token = true;
break;
}
}
}
if (token)
{
continue;
}
if (model[i][] == )
{
MAModel ma = new MAModel(data, model[i][]);
coe = ma.solveCoeOfMA();
type = ;
}
else if (model[i][] == )
{
ARModel ar = new ARModel(data, model[i][]);
coe = ar.solveCoeOfAR();
type = ;
}
else
{
ARMAModel arma = new ARMAModel(data, model[i][], model[i][]);
coe = arma.solveCoeOfARMA();
type = ;
}
double aic = new ARMAMethod().getModelAIC(coe, data, type);
// 在求解過程中如果階數選取過長,可能會出現NAN或者無窮大的情況
if (Double.isFinite(aic) && !Double.isNaN(aic) && aic < minAIC)
{
minAIC = aic;
bestModel[] = model[i][];
bestModel[] = model[i][];
bestModel[] = (int)Math.round(minAIC);
this.arimaCoe = coe;
}
}
return bestModel;
}
public int aftDeal(int predictValue, int period)
{
if (period >= originalData.length)
{
period = ;
}
switch (period)
{
case :
return (int)predictValue;
case :
return (int)(predictValue + originalData[originalData.length - ]);
case :
default:
return (int)(predictValue + originalData[originalData.length - ]);
}
}
public int predictValue(int p, int q, int period)
{
double [] data = this.preDealDiff(period);
int n = data.length;
int predict = ;
double tmpAR = , tmpMA = ;
double [] errData = new double[q + ];
Random random = new Random();
if (p == )
{
double [] maCoe = this.arimaCoe.get();
for(int k = q; k < n; ++k)
{
tmpMA = ;
for(int i = ; i <= q; ++i)
{
tmpMA += maCoe[i] * errData[i];
}
//産生各個時刻的噪聲
for(int j = q; j > ; --j)
{
errData[j] = errData[j - ];
}
errData[] = random.nextGaussian()*Math.sqrt(maCoe[]);
}
predict = (int)(tmpMA); //産生預測
}
else if (q == )
{
double [] arCoe = this.arimaCoe.get();
for(int k = p; k < n; ++k)
{
tmpAR = ;
for(int i = ; i < p; ++i)
{
tmpAR += arCoe[i] * data[k - i - ];
}
}
predict = (int)(tmpAR);
}
else
{
double [] arCoe = this.arimaCoe.get();
double [] maCoe = this.arimaCoe.get();
for(int k = p; k < n; ++k)
{
tmpAR = ;
tmpMA = ;
for(int i = ; i < p; ++i)
{
tmpAR += arCoe[i] * data[k- i - ];
}
for(int i = ; i <= q; ++i)
{
tmpMA += maCoe[i] * errData[i];
}
//産生各個時刻的噪聲
for(int j = q; j > ; --j)
{
errData[j] = errData[j-];
}
errData[] = random.nextGaussian() * Math.sqrt(maCoe[]);
}
predict = (int)(tmpAR + tmpMA);
}
return predict;
}
}
- 主類入口
package arima;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
public class Main
{
public static void main(String args[])
{
Path path = Paths.get("./data/", "data.txt");
File file = path.toFile();
try
(
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)))
)
{
String line = null;
ArrayList<Double> al=new ArrayList<Double>();
while ((line = br.readLine()) != null)
{
al.add(Double.parseDouble(line));
}
double [] data = new double[al.size()];
for (int i = ; i < data.length; ++i)
{
data[i] = al.get(i);
}
ARIMAModel arima = new ARIMAModel(data);
ArrayList<int []> list = new ArrayList<>();
int period = ;
int modelCnt = , cnt = ; //通過多次預測的平均值作為預測值
int [] tmpPredict = new int [modelCnt];
for (int k = ; k < modelCnt; ++k) //控制通過多少組參數進行計算最終的結果
{
int [] bestModel = arima.getARIMAModel(period, list, (k == ) ? false : true);
if (bestModel.length == )
{
tmpPredict[k] = (int)data[data.length - period];
cnt++;
break;
}
else
{
int predictDiff = arima.predictValue(bestModel[], bestModel[], period);
tmpPredict[k] = arima.aftDeal(predictDiff, period);
cnt++;
}
System.out.println("BestModel is " + bestModel[] + " " + bestModel[]);
list.add(bestModel);
}
al.clear();
double sumPredict = ;
for (int k = ; k < cnt; ++k)
{
sumPredict += (double)tmpPredict[k] / (double)cnt;
}
int predict = (int)Math.round(sumPredict);
System.out.println("Predict value="+predict);
}
catch (FileNotFoundException fnfe)
{
fnfe.printStackTrace();
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
注:本文中有部分内容參考自瀚海小松的部落格,大部分内容屬于自己原創。同時自己也是首次接觸到預測模型,可能對其中的相關知識存在了解性錯誤,如果博友發現,請通過部落格聯系我,忘不吝賜教。如果有博友覺着代碼對自己有用,本人已上傳到我的github,可前往下載下傳。