时间序列分析之ARIMA模型的JAVA实现

package com.zz.meridian.utils.algorithm.arima;

import java.util.Vector;

public class AR {
	
	double[] stdoriginalData={};
	int p;
	ARMAMath armamath=new ARMAMath();
	
	/**
	 * AR模型
	 * @param stdoriginalData
	 * @param p //p为MA模型阶数
	 */
	public AR(double [] stdoriginalData,int p)
	{
		this.stdoriginalData=stdoriginalData;
		this.p=p;
	}
/**
 * 返回AR模型参数
 * @return
 */
	public Vector<double[]> ARmodel()
	{
		Vector<double[]> v=new Vector<double[]>();
		v.add(armamath.parcorrCompute(stdoriginalData, p, 0));
		return v;//得到了自回归系数
		
		//还要估计方差项吗?
	}
	
}

package com.zz.meridian.utils.algorithm.arima;


import com.alibaba.fastjson.JSON;

import java.util.List;
import java.util.Random;
import java.util.Vector;


public class ARIMA {

	double[] originalData={};
	ARMAMath armamath=new ARMAMath();
	double stderrDara=0;
	double avgsumData=0;
	Vector<double[]> armaARMAcoe=new Vector<double[]>();
	Vector<double[]> bestarmaARMAcoe=new Vector<double[]>();
	
/**
 * 构造函数
 * @param originalData 原始时间序列数据
 */
	public ARIMA(double [] originalData)
	{
		this.originalData=originalData;
	}
/**
 * 原始数据标准化处理:一阶季节性差分
 * @return 差分过后的数据
 */ 
	public double[] preDealDif()
	{
		
		//seasonal Difference:Peroid=7
		double []tempData=new double[originalData.length-7];
		for(int i=0;i<originalData.length-7;i++)
		{
			tempData[i]=originalData[i+7]-originalData[i];
		}

		return tempData;
	}
/**
 * 原始数据标准化处理:Z-Score归一化
 *  待处理数据
 * @return 归一化过后的数据
 */
	public double[] preDealNor(double[] tempData)
	{
		//Z-Score
		avgsumData=armamath.avgData(tempData);
		stderrDara=armamath.stderrData(tempData);
		
		for(int i=0;i<tempData.length;i++)
		{
			tempData[i]=(tempData[i]-avgsumData)/stderrDara;
		}
		
		return tempData;
	}
/**
* 得到ARMA模型=[p,q]
 * @return ARMA模型的阶数信息
 */
	public int[] getARIMAmodel()
	{
		double[] stdoriginalData=this.preDealDif();//原始数据差分处理
		
		int paraType=0;
		double minAIC=9999999;
		int bestModelindex=0;
		int[][] model=new int[][]{{0,1},{1,0},{1,1},{0,2},{2,0},{2,2},{1,2},{2,1}};//,{3,0},{0,3},{3,1},{1,3},{3,2},{2,3},{3,3}};//,{4,0},{0,4},{4,1},{1,4},{4,2},{2,4},{4,3},{3,4},{4,4}};
		//对8种模型进行迭代,选出AIC值最小的模型作为我们的模型
		for(int i=0;i<model.length;i++)
		{
			if(model[i][0]==0)
			{
				MA ma=new MA(stdoriginalData, model[i][1]);
				armaARMAcoe=ma.MAmodel(); //拿到ma模型的参数
				paraType=1;
			}
			else if(model[i][1]==0)
			{
				AR ar=new AR(stdoriginalData, model[i][0]);
				armaARMAcoe=ar.ARmodel(); //拿到ar模型的参数
				paraType=2;
			}
			else
			{
				ARMA arma=new ARMA(stdoriginalData, model[i][0], model[i][1]);
				armaARMAcoe=arma.ARMAmodel();//拿到arma模型的参数
				paraType=3;
			}
			

			double temp=getmodelAIC(armaARMAcoe,stdoriginalData,paraType);
			System.out.println("AIC of these model="+temp);
			if (temp<minAIC)
			{
				bestModelindex=i;
				minAIC=temp;
				bestarmaARMAcoe=armaARMAcoe;
			}
		}
		
		return model[bestModelindex];
 	}
/**
 * 计算ARMA模型的AIC
 * @param para 装载模型的参数信息
 * @param stdoriginalData   预处理过后的原始数据
 * @param type 1:MA;2:AR;3:ARMA
 * @return 模型的AIC值
 */
	public double getmodelAIC(Vector<double[]> para,double[] stdoriginalData,int type)
	{
		double temp=0;
		double temp2=0;
		double sumerr=0;
		int p=0;//ar1,ar2,...,sig2
		int q=0;//sig2,ma1,ma2...
		int n=stdoriginalData.length;
		Random random=new Random();
		
		if(type==1)
		{
			double[] maPara=para.get(0);
			q=maPara.length;
			double[] err=new double[q];  //error(t),error(t-1),error(t-2)...
			for(int k=q-1;k<n;k++)
			{
				temp=0;
				
				for(int i=1;i<q;i++)
				{
					temp+=maPara[i]*err[i];
				}
			
				//产生各个时刻的噪声
				for(int j=q-1;j>0;j--)
				{
					err[j]=err[j-1];
				}
				err[0]=random.nextGaussian()*Math.sqrt(maPara[0]);
				
				//估计的方差之和
				sumerr+=(stdoriginalData[k]-(temp))*(stdoriginalData[k]-(temp));
				
			}
			//return  (n-(q-1))*Math.log(sumerr/(n-(q-1)))+(q)*Math.log(n-(q-1));//AIC 最小二乘估计
			return (n-(q-1))*Math.log(sumerr/(n-(q-1)))+(q+1)*2;
		}
		else if(type==2)
		{
			double[] arPara=para.get(0);
			p=arPara.length;
			for(int k=p-1;k<n;k++)
			{
				temp=0;
				for(int i=0;i<p-1;i++)
				{
					temp+=arPara[i]*stdoriginalData[k-i-1];
				}
				//估计的方差之和
				sumerr+=(stdoriginalData[k]-temp)*(stdoriginalData[k]-temp);
			}
			return (n-(q-1))*Math.log(sumerr/(n-(q-1)))+(p+1)*2;
			//return (n-(p-1))*Math.log(sumerr/(n-(p-1)))+(p)*Math.log(n-(p-1));//AIC 最小二乘估计
		}
		else
		{
			double[] arPara=para.get(0);
			double[] maPara=para.get(1);
			p=arPara.length;
			q=maPara.length;
			double[] err=new double[q];  //error(t),error(t-1),error(t-2)...
			
			for(int k=p-1;k<n;k++)
			{
				temp=0;
				temp2=0;
				for(int i=0;i<p-1;i++)
				{
					temp+=arPara[i]*stdoriginalData[k-i-1];
				}
			
				for(int i=1;i<q;i++)
				{
					temp2+=maPara[i]*err[i];
				}
			
				//产生各个时刻的噪声
				for(int j=q-1;j>0;j--)
				{
					err[j]=err[j-1];
				}
				//System.out.println("predictBeforeDiff="+1);
				err[0]=random.nextGaussian()*Math.sqrt(maPara[0]);
				//估计的方差之和
				sumerr+=(stdoriginalData[k]-(temp2+temp))*(stdoriginalData[k]-(temp2+temp));
			}
			return (n-(q-1))*Math.log(sumerr/(n-(q-1)))+(p+q)*2;
			//return (n-(p-1))*Math.log(sumerr/(n-(p-1)))+(p+q-1)*Math.log(n-(p-1));//AIC 最小二乘估计
		}
	}
/**
 * 对预测值进行反差分处理
 * @param predictValue 预测的值
 * @return 反差分过后的预测值
 */
	public int aftDeal(int predictValue)
	{
		//System.out.println("predictBeforeDiff="+predictValue);
		return (int)(predictValue+originalData[originalData.length-7]);
	}
/**
 * 进行一步预测
 * @param p ARMA模型的AR的阶数
 * @param q ARMA模型的MA的阶数
 * @return 预测值
 */
	public int predictValue(int p,int q)
	{
		int predict=0;
		double[] stdoriginalData=this.preDealDif();
		int n=stdoriginalData.length;
		double temp=0,temp2=0;
		double[] err=new double[q+1];
	
		Random random=new Random();
		if(p==0)
		{
			double[] maPara=bestarmaARMAcoe.get(0);
			for(int k=q;k<n;k++)
			{
				temp=0;
				for(int i=1;i<=q;i++)
				{
					temp+=maPara[i]*err[i];
				}
				//产生各个时刻的噪声
				for(int j=q;j>0;j--)
				{
					err[j]=err[j-1];
				}
				err[0]=random.nextGaussian()*Math.sqrt(maPara[0]);
			}
			predict=(int)(temp); //产生预测
		}
		else if(q==0)
		{
			double[] arPara=bestarmaARMAcoe.get(0);
			for(int k=p;k<n;k++)
			{
				temp=0;
				for(int i=0;i<p;i++)
				{
					temp+=arPara[i]*stdoriginalData[k-i-1];
				}
			}
			predict=(int)(temp);
		}
		else
		{

			double[] arPara=bestarmaARMAcoe.get(0);
			double[] maPara=bestarmaARMAcoe.get(1);
			err=new double[q+1];  //error(t),error(t-1),error(t-2)...
			for(int k=p;k<n;k++)
			{
				temp=0;
				temp2=0;
				for(int i=0;i<p;i++)
				{
					temp+=arPara[i]*stdoriginalData[k-i-1];
				}
			
				for(int i=1;i<=q;i++)
				{
					temp2+=maPara[i]*err[i];
				}
			
				//产生各个时刻的噪声
				for(int j=q;j>0;j--)
				{
					err[j]=err[j-1];
				}
				
				err[0]=random.nextGaussian()*Math.sqrt(maPara[0]);
			}
			
			predict=(int)(temp2+temp);
			
		}
	
		
		return predict;
	}
/**
 * 计算MA模型的参数
 * @param autocorData 自相关系数Grma
 * @param q MA模型的阶数
 * @return 返回MA模型的参数
 */
	public double[] getMApara(double[] autocorData,int q)
	{
		double[] maPara=new double[q+1];//第一个存放噪声参数,后面q个存放ma参数sigma2,ma1,ma2...
		double[] tempmaPara=maPara;
		double temp=0;
		boolean iterationFlag=true;
		//解方程组
		//迭代法解方程组
		System.out.println("autocorData[0]"+autocorData[0]);
		while(iterationFlag)
		{
			for(int i=1;i<maPara.length;i++)
			{
				temp+=maPara[i]*maPara[i];
			}
			tempmaPara[0]=autocorData[0]/(1+temp);
		
			for(int i=1;i<maPara.length;i++)
			{
				temp=0;
				for(int j=1;j<maPara.length-i;j++)
				{
					temp+=maPara[j]*maPara[j+i];
				}
				tempmaPara[i]=-(autocorData[i]/tempmaPara[0]-temp);
			}
			iterationFlag=false;
			for(int i=0;i<maPara.length;i++)
			{
				if(maPara[i]!=tempmaPara[i])
				{
					iterationFlag=true;
					break;
				}
			}
			
			maPara=tempmaPara;
		}
		
		return maPara;
	}
	public static int[] generator(int size, List<Double> arraylist){

		int[] returnArray=new int[size];

		for (int i = 0; i <size ; i++) {
			double[] dataArray=new double[arraylist.size()];
			for(int j=0;j<arraylist.size()-1;j++) {
				dataArray[j] = arraylist.get(j);
			}
			//使用当前数据训练ARIMA模型
			ARIMA arima=new ARIMA(dataArray);
			//获得ARIM模型
			int []model=arima.getARIMAmodel();
			System.out.println("Best model is [p,q]="+"["+model[0]+" "+model[1]+"]");
			System.out.println("最佳模型是[p,q]="+"["+model[0]+" "+model[1]+"]");
			int deal = arima.aftDeal(arima.predictValue(model[0], model[1]));
			System.out.println("Predict value="+deal);
			System.out.println("预测值="+deal);
			System.out.println("Predict error="+(deal-arraylist.get(arraylist.size()-1))/arraylist.get(arraylist.size()-1)*100+"%");
			System.out.println("预测差值="+(deal-arraylist.get(arraylist.size()-1))/arraylist.get(arraylist.size()-1)*100+"%");
			arraylist.add(deal+0.0);
			returnArray[i]=deal;
		}
		System.out.println(JSON.toJSON(arraylist));
		System.out.println(JSON.toJSON(returnArray));
		return returnArray;
	}
}

package com.zz.meridian.utils.algorithm.arima;

import java.util.Vector;

public class ARMA {
	
	double[] stdoriginalData={};
	int p;
	int q;
	ARMAMath armamath=new ARMAMath();
	
	/**
	 * ARMA模型
	 * @param stdoriginalData
	 * @param p,q //p,q为MA模型阶数
	 */
	public ARMA(double [] stdoriginalData,int p,int q)
	{
		this.stdoriginalData=stdoriginalData;
		this.p=p;
		this.q=q;	
	}
	public Vector<double[]> ARMAmodel()
	{
	
		double[] arcoe=armamath.parcorrCompute(stdoriginalData, p, q);
		
		double[] autocorData=getautocorofMA(p, q, stdoriginalData, arcoe);
		
		double[] macoe=armamath.getMApara(autocorData, q);//得到MA模型里面的参数值
//		for(int i=0;i<macoe.length;i++)
//			{
//				System.out.println(macoe[i]);
//			}
//		System.out.println();
		Vector<double[]> v=new Vector<double[]>();
		v.add(arcoe);
		v.add(macoe);
		return v;
	}
	
	/**
	 * 得到MA的自相关系数
	 * @param p
	 * @param q
	 * @param stdoriginalData
	 * @param autoRegress
	 * @return
	 */
	public double[] getautocorofMA(int p,int q,double[] stdoriginalData,double[] autoRegress)
	{
		int temp=0;
		double[] errArray=new double[stdoriginalData.length-p];
		int count=0;
		for(int i=p;i<stdoriginalData.length;i++)
		{
			temp=0;
			for(int j=1;j<=p;j++)
				temp+=stdoriginalData[i-j]*autoRegress[j-1];
			errArray[count++]=stdoriginalData[i]-temp;//保存估计残差序列
		}
		return armamath.autocorGrma(errArray, q);
	}
}

package com.zz.meridian.utils.algorithm.arima;

import Jama.Matrix;

public class ARMAMath
{
	public double avgData(double[] dataArray)
	{
		return this.sumData(dataArray)/dataArray.length;
	}
	
	public double sumData(double[] dataArray)
	{
		double sumData=0;
		for(int i=0;i<dataArray.length;i++)
		{
			sumData+=dataArray[i];
		}
		return sumData;
	}
	
	public double stderrData(double[] dataArray)
	{
		return Math.sqrt(this.varerrData(dataArray));
	}
	
	public double varerrData(double[] dataArray)
	{
		double variance=0;
		double avgsumData=this.avgData(dataArray);
		
		for(int i=0;i<dataArray.length;i++)
		{
			dataArray[i]-=avgsumData;
			variance+=dataArray[i]*dataArray[i];
		}
		return variance/dataArray.length;//variance error;
	}
	
	/**
	 * 计算自相关的函数 Tho(k)=Grma(k)/Grma(0)
	 * @param dataArray 数列
	 * @param order 阶数
	 * @return
	 */
	public double[] autocorData(double[] dataArray,int order)
	{
		double[] autoCor=new double[order+1];
		double varData=this.varerrData(dataArray);//标准化过后的方差
		
		for(int i=0;i<=order;i++)
		{
			autoCor[i]=0;
			for(int j=0;j<dataArray.length-i;j++)
			{
				autoCor[i]+=dataArray[j+i]*dataArray[j];
			}
			autoCor[i]/=dataArray.length;
			autoCor[i]/=varData;
		}
		return autoCor;
	}
	
/**
 * Grma
 * @param dataArray
 * @param order
 * @return 序列的自相关系数
 */
	public double[] autocorGrma(double[] dataArray,int order)
	{
		double[] autoCor=new double[order+1];
		for(int i=0;i<=order;i++)
		{
			autoCor[i]=0;
			for(int j=0;j<dataArray.length-i;j++)
			{
				autoCor[i]+=dataArray[j+i]*dataArray[j];
			}
			autoCor[i]/=(dataArray.length-i);
			
		}
		return autoCor;
	}
	
/**
 * 求偏自相关系数
 * @param dataArray
 * @param order
 * @return
 */
	public double[] parautocorData(double[] dataArray,int order)
	{
		double parautocor[]=new double[order];
		
		for(int i=1;i<=order;i++)
	    {
			parautocor[i-1]=this.parcorrCompute(dataArray, i,0)[i-1];
	    }
		return parautocor;
	}
/**
 * 产生Toplize矩阵
 * @param dataArray
 * @param order
 * @return
 */
	public double[][] toplize(double[] dataArray,int order)
	{//返回toplize二维数组
		double[][] toplizeMatrix=new double[order][order];
		double[] atuocorr=this.autocorData(dataArray,order);

		for(int i=1;i<=order;i++)
		{
			int k=1;
			for(int j=i-1;j>0;j--)
			{
				toplizeMatrix[i-1][j-1]=atuocorr[k++];
			}
			toplizeMatrix[i-1][i-1]=1;
			int kk=1;
			for(int j=i;j<order;j++)
			{
				toplizeMatrix[i-1][j]=atuocorr[kk++];
			}
		}
		return toplizeMatrix;
	}

	/**
	 * 解MA模型的参数
	 * @param autocorData
	 * @param q
	 * @return
	 */
	public double[] getMApara(double[] autocorData,int q)
	{
		double[] maPara=new double[q+1];//第一个存放噪声参数,后面q个存放ma参数sigma2,ma1,ma2...
		double[] tempmaPara=maPara;
		double temp=0;
		boolean iterationFlag=true;
		//解方程组
		//迭代法解方程组
		maPara[0]=1;//初始化
		while(iterationFlag)
		{
			for(int i=1;i<maPara.length;i++)
			{
				temp+=maPara[i]*maPara[i];
			}
			tempmaPara[0]=autocorData[0]/(1+temp);
		
			for(int i=1;i<maPara.length;i++)
			{
				temp=0;
				for(int j=1;j<maPara.length-i;j++)
				{
					temp+=maPara[j]*maPara[j+i];
				}
				tempmaPara[i]=-(autocorData[i]/maPara[0]-temp);
			}
			iterationFlag=false;
			for(int i=0;i<maPara.length;i++)
			{
				if(maPara[i]!=tempmaPara[i])
				{
					iterationFlag=true;
					break;
				}
			}
			
			maPara=tempmaPara;
		}
		
		return maPara;
	}
	/**
	 * 计算自回归系数
	 * @param dataArray
	 * @param p
	 * @param q
	 * @return
	 */
	public double[] parcorrCompute(double[] dataArray,int p,int q)
	{
		double[][] toplizeArray=new double[p][p];//p阶toplize矩阵;
		
		double[] atuocorr=this.autocorData(dataArray,p+q);//返回p+q阶的自相关函数
		double[] autocorrF=this.autocorGrma(dataArray, p+q);//返回p+q阶的自相关系数数
		for(int i=1;i<=p;i++)
		{
			int k=1;
			for(int j=i-1;j>0;j--)
			{
				toplizeArray[i-1][j-1]=atuocorr[q+k++];
			}
			toplizeArray[i-1][i-1]=atuocorr[q];
			int kk=1;
			for(int j=i;j<p;j++)
			{
				toplizeArray[i-1][j]=atuocorr[q+kk++];
			}
		}
		
	    Matrix toplizeMatrix = new Matrix(toplizeArray);//由二位数组转换成二维矩阵
	    Matrix toplizeMatrixinverse=toplizeMatrix.inverse();//矩阵求逆运算
		
	    double[] temp=new double[p];
	    for(int i=1;i<=p;i++)
	    {
	    	temp[i-1]=atuocorr[q+i];
	    }
	    
		Matrix autocorrMatrix=new Matrix(temp, p);
		Matrix parautocorDataMatrix=toplizeMatrixinverse.times(autocorrMatrix); //  [Fi]=[toplize]x[autocorr]';
		//矩阵计算结果应该是按照[a b c]'  列向量存储的
		//System.out.println("row="+parautocorDataMatrix.getRowDimension()+"  Col="+parautocorDataMatrix.getColumnDimension());
		//parautocorDataMatrix.print(p, 2);//(输出几行,小数点后保留位数)
		//System.out.println(parautocorDataMatrix.get(p-1,0));
		
		double[] result=new double[parautocorDataMatrix.getRowDimension()+1];
		for(int i=0;i<parautocorDataMatrix.getRowDimension();i++)
		{
			result[i]=parautocorDataMatrix.get(i,0);
		}
		
		//估算sigmat2
		double sum2=0;
		for(int i=0;i<p;i++)
			for(int j=0;j<p;j++)
			{
				sum2+=result[i]*result[j]*autocorrF[Math.abs(i-j)];
			}
		result[result.length-1]=autocorrF[0]-sum2; //result数组最后一个存储干扰估计值
		
		
			return result;   //返回0列的最后一个就是k阶的偏自相关系数 pcorr[k]=返回值
	}

	
	}

package com.zz.meridian.utils.algorithm.arima;

import java.util.Vector;

public class MA {

	double[] stdoriginalData={};
	int q;
	ARMAMath armamath=new ARMAMath();
	
	/** MA模型
	 * @param stdoriginalData //预处理过后的数据
	 * @param q //q为MA模型阶数
	 */
	public MA(double [] stdoriginalData,int q)
	{
		this.stdoriginalData=stdoriginalData;
		this.q=q;
	}
/**
 * 返回MA模型参数
 * @return
 */
	public Vector<double[]> MAmodel()
	{
		Vector<double[]> v=new Vector<double[]>();
		v.add(armamath.getMApara(armamath.autocorGrma(stdoriginalData,q), q));
		return v;//拿到MA模型里面的参数值
	}
		
	
}

package com.zz.meridian.utils.algorithm.arima;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

import static com.alibaba.druid.sql.ast.SQLPartitionValue.Operator.List;

public class Test1 {

    public static void main(String args[]) {
//		extracted();
		ArrayList<Double> arraylist = new ArrayList<Double>();
		arraylist.add(1.0d);
		arraylist.add(2.0d);
		arraylist.add(3.0d);
		arraylist.add(4.0d);
		arraylist.add(5.0d);
		arraylist.add(6.0d);
		arraylist.add(7.0d);
		arraylist.add(8.0d);
		arraylist.add(8.0d);
		arraylist.add(8.0d);
		arraylist.add(8.0d);
		arraylist.add(8.0d);
		int[] ints = ARIMALists(arraylist);
		System.err.println(Arrays.toString(ints));
	}

	private static int[] ARIMALists(ArrayList<Double> arraylist) {
    	if (arraylist.size()<10){
			System.out.println("创建ARIMA模型数据最少10条");
    		return null;
		}
		int[] ints = ARIMA.generator2(5, arraylist);
		return ints;
	}

	private static void extracted() {
		Scanner ino = null;

		try {
			ArrayList<Double> arraylist = new ArrayList<Double>();
			ino = new Scanner(new File("E:\\java_project\\siweikeji\\meridian\\code\\meridian\\src\\main\\java\\com\\zz\\meridian\\utils\\algorithm\\arima\\ceshidata1.txt"));
			while (ino.hasNext()) {
				arraylist.add(Double.parseDouble(ino.next()));
			}
			double[] dataArray = new double[arraylist.size() - 1];
			for (int i = 0; i < arraylist.size() - 1; i++) {
				dataArray[i] = arraylist.get(i);
			}

			ARIMA.generator(5, arraylist);
			//System.out.println(arraylist.size());
//			ARIMA arima=new ARIMA(dataArray);
//
//			int []model=arima.getARIMAmodel();
//			System.out.println("Best model is [p,q]="+"["+model[0]+" "+model[1]+"]");
//			System.out.println("Predict value="+arima.aftDeal(arima.predictValue(model[0],model[1])));
//			System.out.println("Predict error="+(arima.aftDeal(arima.predictValue(model[0],model[1]))-arraylist.get(arraylist.size()-1))/arraylist.get(arraylist.size()-1)*100+"%");
//
			//	String[] str = (String[])list1.toArray(new String[0]);

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			ino.close();
		}
	}


}

ceshidata1.txt文件
1156535
1291442
1359713
1374810
1416035
1528192
1521013
1156535
1291442
1359713
1374810
1455568
1534559
1517461
1384346

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值