2018华为软件精英挑战赛

本文详细解析了华为软件设计大赛的实战经验,包括初赛和复赛的预测、分配及优化策略,介绍了灰度预测、模拟退火算法等关键技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、初赛

1.去噪

2.预测

3.分配

4.优化分配

5.优化预测

二、复赛

1.分析

2.预测

3.分配

三、总结


先看看题目,华为软挑题目详情

一、初赛

先解析输入文件,按照他要求格式输出文件。预测数据且分配只考虑CPU或内存的利用率

评分标准如下:

 

1.去噪

去噪一开始用的是lameda法则,根据实际期望上下超过3lameda的数据视为异常值。

2.预测

因为数据比较短只有一个多月的单一数据。在网上搜寻了一下时间序列预测的一些方法,都尝试了一下,发现灰度预测效果比较好。不懂的可以参考灰度预测讲解和代码实现,灰色预测模型(Gray Forecast Model)是通过少量的、不完全的信息,建立数学模型并做出预测的一种预测方法。

预测出n天的单日数据,然后加起来,就得到了n天的总和。

3.分配

每次只让CPU或内存最大即可,考虑到了使用0-1背包。把需要优化最大的作为背包价值,另一个作为背包容量。但是没有完全符合的代码,只能自己理解背包的内涵,修改一下。

具体的分析在这篇文章里面:背包改动版。依次装背包,装满一个就从资源中剔除掉装入的部分。把剩余的资源再装背包。不停重复这个过程,直到资源没有剩余。

这样就可以完成从预测到分配的整个过程。这样可以得70分。

	
		/**
		 * Each time the linklist's data is converted into an array, starts boxing and then updates, 
		 * deleting the elements that have been packed in the three lists.
		 *
		 */
		int totalBoxNumIndex = resultsIndex;
		//Record the index position, set the total number of cycles to be 
		//stored here after the loop is completed		
		results[resultsIndex++] = "totalBox";
		
		int packingCount = 0;
		int predictedCpuSum = 0;
		int predictedRamSum = 0;
		int[] arr = new int[2];
		while(!flavorList.isEmpty()){
		 
			int[] ramArray = new int[ramList.size()] ;
			int[] cpuArray = new int[cpuList.size()] ;
			
			for(int i=0;i<ramList.size();i++){		
				ramArray[i] = ramList.get(i);		
				predictedRamSum+=ramList.get(i);
			}
			for(int i=0;i<cpuList.size();i++){		
				cpuArray[i] = cpuList.get(i);
				predictedCpuSum+=cpuList.get(i);
			}
			System.out.println("weight"+Arrays.toString(ramArray));
			System.out.println("value"+Arrays.toString(cpuArray));
			ArrayList<Integer> packedFlavorList = new ArrayList<Integer>();
			//The list packedFlavorList indicates the position of the removed element
			
			//Explain that the memory space is even more scarce, giving priority to full memory
			System.out.println("cpuSum:"+predictedCpuSum+" memSum:"+predictedRamSum);
			if(((double)predictedCpuSum)/predictedRamSum <=ratioOfCpuAndRam){
				//Input w_l,w[],v,v_l
				System.out.println("giving priority to full memory");
				packedFlavorList = knapsackOptimal(cpuLimit,cpuArray,ramArray,ramLimit);
			}else{
				//giving priority to full cpu
				System.out.println("giving priority to full cpu");
				packedFlavorList = knapsackOptimal(ramLimit,ramArray,cpuArray,cpuLimit);
			}
			
			System.out.println("packedFlavorList:");
			for(int i=0;i<packedFlavorList.size();i++){
				System.out.print(packedFlavorList.get(i)+" ");
			}
			System.out.println();
			int[] sum = new int[needPredictedFlavorType.length];
			for(int i=0;i<packedFlavorList.size();i++){
				int pack_index = packedFlavorList.get(i);//Index returned, minimum is 0
				for(int j=0;j<needPredictedFlavorType.length;j++){
					if(flavorList.get(pack_index)==needPredictedFlavorType[j]){
						System.out.print(packedFlavorList.get(i)+","+needPredictedFlavorType[j]+"|");
						sum[j]+=1;
					}
				}
			}
			System.out.println();
			System.out.println("The number of picked-flavors of each type "+Arrays.toString(sum));			
			results[resultsIndex]=++packingCount+"";
			//Record in results
			for(int i=0;i<sum.length;i++){
				if(sum[i]!=0){
					results[resultsIndex] +=" flavor"+needPredictedFlavorType[i]+" "+sum[i];
				}				
			}
			resultsIndex++;
			
			//update three lists			
			arr = deletePackedFlavor(flavorList,cpuList,ramList,packedFlavorList,predictedCpuSum,predictedRamSum);
			predictedCpuSum = arr[0];
			predictedRamSum = arr[1];
			System.out.println("leftcpuSum:"+predictedCpuSum+" leftmemSum:"+predictedRamSum);
			System.out.println("nextPacking");
		}
		results[totalBoxNumIndex] = packingCount+"";


后来,时候跟别的队伍交流已经发现自己的代码存在严重的问题,许多队伍已经可以达到80分了。

预测这块的话大家都不是很准,主要拉开差距的是分配这块。前面的背包分配有一个很大的问题就是非常容易陷入局部最优解。刚开始我想不通,后来意识到,虽然每次取出的都是最大限度能装满当前背包的价值,但是最后一个或许很空,没有完全考虑了所有背包(总共的资源)的整体情况,或许交换了一下其中的服务器,能让总体的利用率下降。当时没想这么透彻,抱着试一试的心态,参考了网上的大佬的想法,从整体入手,更改了分配算法。

4.优化分配

采用模拟退火算法,每次随机交换两个flavor的顺序,按照顺序遍历Flavor,将flavor依次装入箱子,能装下就装,遍历完成即就结束当前箱子的装配,换新的箱子装。优化的评估函数为指定优化的cpu或内存的利用率。退火结束后有较好的效果。实际效果确实更好,分配空间利用率更高,得分也更高。

其次,为了逼近更高的填充率,对所有箱子尝试填充flavor,这对预测改动很小,但是对利用率提升很大,可以有效的提高分数。

具体的做法是,我会在分配完成后,用退火的方式,一次退火随机向每个服务器填充一个任意信号的flavor,退火结束后可以得到一个较优解,评估函数是得分的评分标准,前半部分预测的评分。以当前预测解为正确值,以填充后的解为预测值。后半部分空间利用率很好计算,不再多说。

随后,我会考虑是否要删除最后一个服务器,看评估函数分会不会更高,最后得到一个填充删减的预测分配相结合的结果,作为最终预测。

class putFlavorsToServers {
	
	private static Boolean CPU = true;
	private static Boolean MEM = false;
	public ArrayList<Server> resServers;
	public putFlavorsToServers(){
		
	}
	public static ArrayList<Server> put(int flavorTypeNumber, int[] needPredictFlavorsType,HashMap<String,Integer> predictFlavorsNum, 
			HashMap<String,Flavor> flavorCpuMemMap,int serverCpu, int serverMem, Boolean CPUorMEM){
		
		int[] eachFlavorsNum = new int[needPredictFlavorsType.length];//各flavor预测数量
		ArrayList<Flavor> predictFlavors = new ArrayList<Flavor>();	
		for(int i=0;i<needPredictFlavorsType.length;i++){
			
			String key = "flavor"+ needPredictFlavorsType[i];		
			int num = predictFlavorsNum.get(key);//预测数量
			eachFlavorsNum[i] = num;
			System.out.println("---------------key--------------"+key+" num:"+ num);
			Flavor flavor = flavorCpuMemMap.get(key);//对应的flavor
			
			while(num--!=0){
				predictFlavors.add(flavor);
			}
		}
		
	//=====================================================模拟退火算法最优解==============================================================================================
		//退火不稳定,多次退火
		ArrayList<Server> bestSAResServers = new ArrayList<Server>();;
		double bestSAScore = 0;

		for(int k=0;k<5;k++){
			
			double minServer = predictFlavors.size()+1;
			ArrayList<Server> resServers = new ArrayList<Server>();//存放结果
			ArrayList<Server> tmpServers = new ArrayList<Server>();//删除结果	
			ArrayList<Server> tmpClipServers = new ArrayList<Server>();//删除结果	
			ArrayList<Server> resClipServers = new ArrayList<Server>() ;//删除结果	
			ArrayList<Server> tmpFillServers = new ArrayList<Server>();//填充结果	
			ArrayList<Server> resFillServers = new ArrayList<Server>();//填充结果	
			double minT = 1;//终止温度
			double T = 1000;//初始温度
			double r = 0.99;
			double origR = 0;//原始利用率
			double clipR = 0;//剪切后的利用率
	//		double SpaceUtilization3 = 0;
			double origScore = 0;
			double maxScore = 0;//总的最高分
			double tmpScore = 0;//当前三者中的最高分
			double tmpOrigScore = 0;
			double tmpClipScore = 0;
			double maxClipScore = 0;
			double tmpFillScore = 0;
			double maxFillScore = 0;
			ArrayList<Integer> dice = new ArrayList<Integer>();//每次退火随机交换两个虚拟机
			for(int i=0;i<predictFlavors.size();i++)
				dice.add(i);
		
			while(T>minT){
				//打乱下标
				Collections.shuffle(dice);	
//				int a = (int) (Math.random()*predictFlavors.size());
//				int b = (int) (Math.random()*predictFlavors.size());
				ArrayList<Flavor> newPredictFlavors = (ArrayList<Flavor>) predictFlavors.clone();
				exchange(newPredictFlavors,dice.get(0),dice.get(1));
//				exchange(newPredictFlavors,a,b);
				
				//把上一步计算出来的虚拟机尝试加入到服务器中

				//先使用一个服务器用于放置虚拟机
				ArrayList<Server> servers = new ArrayList<Server>();;
				Server firstServer = new Server(serverMem,serverCpu);
				servers.add(firstServer);
				
				//放置虚拟机主要逻辑
				//依次放置
				for(Flavor f:newPredictFlavors){
					
					Iterator<Server> iter = servers.iterator();
					Server server = iter.next();;
					
					while(true){	
						
						if(server.putFlavor(f)){
							break;
						}else{
							if(iter.hasNext())
							  server = iter.next();
							else{
								Server newServer = new Server(serverMem,serverCpu);
								newServer.putFlavor(f);
								servers.add(newServer);
								break;
							}				
						}
					}
				}
				
				//模拟退火就是得到取得分数最小的放置方式
				double serverNum = 0;
				
				//CPU还是MEM,对应不同资源利用率计算方法
				if(CPUorMEM ==CPU){
					serverNum = servers.size()-1 + servers.get(servers.size()-1).getCpuUsageRate();
					
					origR = getTotalCpuUsageRate(servers);	
//					clipR = getTotalCpuUsageRate(clipServers);	
					tmpOrigScore = 1*origR;
					
				}else{
					serverNum = servers.size()-1 + servers.get(servers.size()-1).getMemUsageRate();
					origR = getTotalMemUsageRate(servers);
//					clipR = getTotalMemUsageRate(clipServers);	
					tmpOrigScore = 1*origR;
				}
				//clipServers的最高分数
				if(servers.size()>1){
					
					//去掉最后一个服务器
					tmpClipServers = (ArrayList<Server>) servers.clone();
					tmpClipServers.remove(tmpClipServers.size()-1);
					tmpClipScore = fillServers(flavorTypeNumber,eachFlavorsNum, tmpClipServers,
							flavorCpuMemMap, needPredictFlavorsType,CPUorMEM);
								
				}
				
				//fillServers的当前最大值
				tmpFillServers = (ArrayList<Server>) servers.clone();
				tmpFillScore = fillServers(flavorTypeNumber,eachFlavorsNum, tmpFillServers,
						flavorCpuMemMap, needPredictFlavorsType,CPUorMEM);
				
		/**======================================原始server====================================================================== */
				//得分最高的方案是tmpServers
				if(tmpClipScore> tmpFillScore && tmpClipScore > tmpOrigScore){
					tmpScore = tmpClipScore;
					tmpServers = (ArrayList<Server>) tmpClipServers.clone();
				}else if(tmpFillScore > tmpClipScore && tmpFillScore > tmpOrigScore){
					tmpScore = tmpFillScore;
					tmpServers = (ArrayList<Server>) tmpFillServers.clone();
				}else{
					tmpScore = tmpOrigScore;
					tmpServers = (ArrayList<Server>) servers.clone();
				}			
				
					
				//分数更低,保存结果
				if(tmpScore > maxScore){
					maxScore = tmpScore;
					resServers = (ArrayList<Server>) tmpServers.clone();
					predictFlavors = (ArrayList<Flavor>)newPredictFlavors.clone();
				}
				//如果分数更高,则以一定概率保存结果,防止优化陷入局部最优解
				else{
					
					if(Math.exp( (tmpScore-maxScore)/T ) > Math.random()){
						maxScore = tmpScore;
						resServers = (ArrayList<Server>) tmpServers.clone();
						predictFlavors = (ArrayList<Flavor>)newPredictFlavors.clone();
					}
				}					
				T = r*T;
			}
			//如果此次退火分数更高,保存结果
			if(maxScore > bestSAScore ){
				
				bestSAResServers = (ArrayList<Server>) resServers.clone();
			}
			
			
		}
		
		
	/**====================================  一次退火结束,比 较 结 果    ====================================================================== */
		
		return bestSAResServers;
	}
	public static int[] getClipServersEachFlavorNum(int flavorTypeNumber,int[] eachFlavorsNum, 
			int[] needPredictedFlavorType, ArrayList<Flavor> flavors){
		
		int[] sum = new int[flavorTypeNumber];
		for(int j=0;j<flavors.size();j++){		
			int x = Integer.parseInt(flavors.get(j).name.substring(6))-1;//Index returned, minimum is 0
			sum[x]++;			
		}

		int[] lastServerFlavorsNum = new int[needPredictedFlavorType.length];
		int[] clipServersFlavorsNum = new int[needPredictedFlavorType.length];
		for(int i=0;i<lastServerFlavorsNum.length;i++){
			
			int m = needPredictedFlavorType[i];//预测的type,代号
			int lastServerOneFlavorSum = sum[m];
			clipServersFlavorsNum[i] = eachFlavorsNum[i] - lastServerOneFlavorSum;
		}
		
		return clipServersFlavorsNum;	
	}
	/**
	 * 依次随机填入新的flavor,直到放不下为止
	 * @param flavorTypeNumber
	 * @param originalFlavorsNum 预测的flavvor数量
	 * @param servers	布置的服务器
	 * @param flavorCpuMemMap 
	 * @param needPredictFlavorsType
	 * @param CPUorMEM
	 * @return
	 */
	public static double fillServers(int flavorTypeNumber,int[] originalFlavorsNum, ArrayList<Server> servers,
			HashMap<String,Flavor> flavorCpuMemMap, int[] needPredictFlavorsType,Boolean CPUorMEM){
		double minT = 1;//终止温度
		double T = 1000;//初始温度
		double r = 0.99;
		double originalScore = 0;
		double tmpScore = 0;
		double maxScore = 0;
		double originalR = 0;//原始总空间利用率
		double tmpR = 0;//当前总空间利用率
		ArrayList<Server> resultServers;
		ArrayList<Server> serversClone = (ArrayList<Server>) servers.clone();
		int[] tmpFlavorsSum = originalFlavorsNum.clone();
		if(CPUorMEM ==CPU){		
			originalR = getTotalCpuUsageRate(servers);		
		}else{			
			originalR = getTotalMemUsageRate(servers);
		}
		//最大分数初始是未添加任何flavor的布置得分
		originalScore = 1*originalR;
		maxScore = originalScore;
		Boolean stop = false;
		int i= servers.size()-1;//从最后一个server开始填充,填到第一个停止
		while(T>minT){
			
//			for(int i=servers.size();i>=0;i--){
//				
//			}
			stop = false;
			//随机添加一个类型flavor到servers的最后一个服务器中	
			int count = 0;
			while(true){
							
				int ran = (int) (Math.random()*needPredictFlavorsType.length);
				String key = "flavor"+ needPredictFlavorsType[ran];	
				Flavor ranflavor = flavorCpuMemMap.get(key);
				count++;
				//添加成功,退出
				if(servers.get(i).putFlavor(ranflavor)){				
					tmpFlavorsSum[ran]++;
					break;
				}
				//如果循环了多次找不到可以放置的flavor,退出
				if(count>flavorTypeNumber*2){
					stop = true;
					break;
				}
			}
			//当前添加失败,取前一个server,如果没有,则结束退火
			if(stop){
				if(i==0){
					break;
				}else{
					i--;
				}			
			}else{
				//添加成功,评价此操作
				//评价此次添加flavor的得分
				if(CPUorMEM ==CPU){		
					tmpR = getTotalCpuUsageRate(servers);		
				}else{			
					tmpR = getTotalMemUsageRate(servers);
				}
				tmpScore = getScore(originalFlavorsNum,tmpFlavorsSum,tmpR);
				//如果分数更高,则保存结果
				if(tmpScore > maxScore){
					maxScore = tmpScore;			
					resultServers = (ArrayList<Server>) servers.clone();
					
				}else{
					//如果分数更低,则以一定概率保存结果,防止优化陷入局部最优解
					if(Math.exp( (tmpScore-maxScore)/T ) >Math.random()){
						maxScore = tmpScore;			
						resultServers = (ArrayList<Server>) servers.clone();			
					}else{
						//否则不保存结果,且擦除对servers的修改
						servers = (ArrayList<Server>) serversClone.clone();
					}
				}
			}
				
			
			T = r*T;		
		}
		return maxScore;
		
	}
	//获取服务器CPU总使用率
	public static double getTotalCpuUsageRate(ArrayList<Server> Servers){
		
		double rate = 0;
		for(Server s:Servers){
			rate += s.getCpuUsageRate();
		}
		rate /= Servers.size();
		return rate;
	}
	//获取服务器CPU总使用率
	public static double getTotalMemUsageRate(ArrayList<Server> Servers){
		
		double rate = 0;
		for(Server s:Servers){
			rate += s.getMemUsageRate();
		}
		rate /= Servers.size();
		return rate;
	}
	
	/**
	 * 
	 * @param y 	虚拟机规格实际数量
	 * @param y1	虚拟机规格预测数量
	 * @param r		空间利用率
	 * @return
	 */
	public static double getScore(int[] y,int[] y1,double r){
		
		double score = 0;
		double a = 0;
		double b1 = 0;
		double b2 = 0;
		int N = y.length;
		
		//分子
		for(int i=0;i<N;i++){
			a = (y[i]-y1[i])*(y[i]-y1[i]);
		}
		a = (double)a/N;
		a = Math.sqrt(a);
		//分母
		for(int i=0;i<N;i++){
			b1 = y[i]*y[i];
			b2 = y1[i]*y1[i];
		}
		b1 = (double)b1/N;
		b2 = (double)b2/N;
		b1 = Math.sqrt(b1);
		b2 = Math.sqrt(b2);
		
		score = (1 - a/(b1+b2))*r;
		return score;
		
	}
	//double minServer = vecFlavors;
	public static void exchange(ArrayList<Flavor> list,int a,int b){
		Flavor tmp = list.get(a);
		list.set(a, list.get(b));
		list.set(b, tmp);
	}		
	
}

5.优化预测

初赛后期又改进了预测的方法,尝试了线性回归指数平滑,都有不错的效果。

  • 线性回归,尝试了最小二乘法和梯度下降法,发现对预测结果没有差别,都可以采用。线性回归将拟合每日的数据得到线性方程,预测未来多天的单日数据,相加即可得到多日和,
  • 指数平滑的话,利用给出的数据的周和,预测下一周和效果最佳,尝试的平滑系数0.55效果最佳。

二、复赛

服务器的规格从一个增加为三个,即CPU与内存的权重各为0.5。评分标准发生了变化。程序运行时间不超过90s(单个用例);

需要预测的时间跨度为1~4个星期,且要预测的开始时间与训练数据集的结束时间不一定是连续的,会从0~15天后开始预测;

 

1.分析

复赛在初赛的基础上增加了难度。

  1. 预测有跨度间隔。但是因为数据规律性不强,本来就很难预测,这块的影响不是很大。
  2. 服务器规格从1变为3,flavor规格也从15变为18种,增加了分配的难度,且要求90s内结束。这对分配是非常大的考验。

2.预测

沿用了初赛的几种方法,因为数据短,且间隔预测,又尝试了移动平均法,向量机,最后阶段尝试了ARIMA,但是因为能力问题,没能解决确定模型时遇到的一个bug,会导致程序出错,无法预测。最终确定为移动平均法。

3.分配

笨办法

一开始,我沿用初赛的思路,从单一服务器到3规格服务器。从单一的服务器序列1111111变为12312312,这样的序列。我在退火分配外再加了一层退火,负责生成随机的服务器序列。当用某个服务器序列分配时,会得到较好的解。但是实践后发现,这样很费时间,90s内无法大量的尝试分配,结果不稳定,很难得到良好的解。

这时候我开始换思路,考虑简单的方案,能否减掉外层循环。

好方法

查阅文献资料,发现一个有用的方法,首次适应法+模拟退火。对于flavor序列,每次我会尝试用三个不同类型的服务器来填充。每次随机交换两个flavor的顺序,按照顺序遍历Flavor,将flavor依次装入箱子,能装下就装,遍历完成即就结束当前箱子的装配,换新的箱子装。哪个服务器的资源利用率得分更高就采用他,然后把装入的flavor从flavor序列中剔除。如果未装完,循环这个过程。

这样复杂度从O(n2)优化为O(n),实测计算速度大大提升,可以有更多的随机分配次数,更好的逼近最优解。

另一个发现是,对于flavor序列,初始对其shuffle操作,会得到更优解。

class putFlavorsToServers {
	
	
	public ArrayList<Server> resServers;
	public putFlavorsToServers(){
		
	}
	public static ArrayList<Server> put(int flavorTypeNumber, int[] needPredictFlavorsType,
			HashMap<String,Integer> predictFlavorsNum, 
			HashMap<String,Flavor> flavorCpuMemMap,Server[] serverType){
		
		int[] eachFlavorsNum = new int[needPredictFlavorsType.length];//各flavor预测数量
		LinkedList<Flavor> predictFlavors = new LinkedList<Flavor>();	
		for(int i=0;i<needPredictFlavorsType.length;i++){
			
			String key = "flavor"+ needPredictFlavorsType[i];		
			int num = predictFlavorsNum.get(key);//预测数量
			eachFlavorsNum[i] = num;
//			System.out.println("---------------key--------------"+key+" num:"+ num);
			Flavor flavor = flavorCpuMemMap.get(key);//对应的flavor
			
			while(num--!=0){
				predictFlavors.add(flavor);
			}
		}
//		//记录所选的箱子的顺序
//		//添加  0,1,2
//		ArrayList<Integer> serverId = new ArrayList<Integer>();		
//		
//		for(int j=0;j<3;j++){
//			serverId.add(j);
//		}
//		//服务器放置顺序,初始为00000000000000000
//		ArrayList<Integer> serverOrder = new ArrayList<Integer>();
//		for(int i=0;i<100;i++){
//			serverOrder.add(0);
//		}
		
		
		
		
		
	//=============================================   模拟退火算法最优解    ==============================================================================================
		
		double minServer = predictFlavors.size()+1;
		ArrayList<Server> resServers = new ArrayList<Server>();//存放结果
		ArrayList<Server> tmpServers = new ArrayList<Server>();//删除结果	
		ArrayList<Server> tmpClipServers = new ArrayList<Server>();//删除结果	
//		ArrayList<Server> resClipServers = new ArrayList<Server>() ;//删除结果	
		ArrayList<Server> tmpFillServers = new ArrayList<Server>();//填充结果	
//		ArrayList<Server> resFillServers = new ArrayList<Server>();//填充结果	
		double minT = 1;//终止温度
		double T = 20;//初始温度
		double r = 0.90;
		double origR = 0;//原始利用率
		double clipR = 0;//剪切后的利用率
//		double SpaceUtilization3 = 0;
	
		double resScore = 0;//总的最高分
		double tmpScore = 0;//当前三者中的最高分
		double tmpOrigScore = 0;
		double tmpClipScore = 0;
	
		double tmpFillScore = 0;
	

	
		Collections.shuffle(predictFlavors);
		while(T>minT){
						
			LinkedList<Flavor> predictFlavorsShuffled = (LinkedList<Flavor>) predictFlavors.clone();
		
			//交换predictFlavorsShuffled
			for(int h=0;h<3;h++){
				int a = (int) (Math.random()*predictFlavors.size());
				int b = (int) (Math.random()*predictFlavors.size());
				while(a==b){
					b = (int) (Math.random()*predictFlavors.size());
				}
				exchange(predictFlavorsShuffled,a,b);
			}
			LinkedList<Flavor> predictFlavorsShuffledCopy = (LinkedList<Flavor>) predictFlavorsShuffled.clone();
			
			//把上一步计算出来的虚拟机尝试加入到服务器中

//			//先使用一个服务器用于放置虚拟机,随机产生
//			int serverIndex = (int) (Math.random()*3);
			ArrayList<Server> servers = new ArrayList<Server>();
			
			//predictFlavorsShuffled不断减少
			while(!predictFlavorsShuffled.isEmpty()){
				

				double tmpOneServerR = 0;
				
				int onePickBestServer;
				Server bestOnePickServer = new Server( serverType[0].name,
						serverType[0].totalCpu,serverType[0].totalMem);
				
				LinkedList<Flavor> threeTimeBestLeftPredictFlavors = (LinkedList<Flavor>) predictFlavorsShuffled.clone();
				double maxOneSeverR = 0;
				for(int i=0;i<3;i++){
					
					//分别开123服务器
					Server newServer = new Server( serverType[i].name,
							serverType[i].totalCpu,serverType[i].totalMem);
//					servers.add(newServer);
					LinkedList<Flavor> tmpPredictFlavorsToBeRemoved = (LinkedList<Flavor>) predictFlavorsShuffled.clone();
					//放置虚拟机主要逻辑
					//倒序依次放置
					for(int j=predictFlavorsShuffled.size()-1;j>=0;j--){
					
						//server装入了flavor,predictClone删除flavor
						if(newServer.putFlavor(predictFlavorsShuffled.get(j))){		
							
							tmpPredictFlavorsToBeRemoved.remove(j);												
						}					
					}
					
					
				
					tmpOneServerR = getOneServerCpuAndMemUsageRate(newServer);//新服务器
					
					//3种箱子装配,比空间利用率大小
					if(tmpOneServerR > maxOneSeverR){
						//保存三次放置三个箱子的最优解
						maxOneSeverR = tmpOneServerR;
						bestOnePickServer = newServer;//选择哪种箱子
						//对应装入,剩下的flavors不同	
						threeTimeBestLeftPredictFlavors = (LinkedList<Flavor>) tmpPredictFlavorsToBeRemoved.clone();
					}
						
				}
				//保存三个箱子中较好的结果,并且更新predictFlavorsShuffled.
				predictFlavorsShuffled = (LinkedList<Flavor>) threeTimeBestLeftPredictFlavors.clone();
				//添加当前最好的结果箱子
				servers.add(bestOnePickServer);
//					clipR = getTotalCpuUsageRate(clipServers);	
			}
			//一次退火,放置箱子结束,计算总的利用率
			tmpOrigScore = getTotalCpuAndMemUsageRate(servers);
			
			//clipServers的最高分数
			if(servers.size()>1){
				
				//去掉最后一个服务器
				tmpClipServers = (ArrayList<Server>) servers.clone();
				tmpClipServers.remove(tmpClipServers.size()-1);
				tmpClipScore = fillServers(flavorTypeNumber,eachFlavorsNum, tmpClipServers,
						flavorCpuMemMap, needPredictFlavorsType);		
			}
			
			//fillServers的当前最大值
			tmpFillServers = (ArrayList<Server>) servers.clone();
			tmpFillScore = fillServers(flavorTypeNumber,eachFlavorsNum, tmpFillServers,
					flavorCpuMemMap, needPredictFlavorsType);
			
	/**======================================原始server====================================================================== */
			//得分最高的方案是tmpServers
			if(tmpClipScore> tmpFillScore && tmpClipScore > tmpOrigScore){
				tmpScore = tmpClipScore;
				tmpServers = (ArrayList<Server>) tmpClipServers.clone();
			}else if(tmpFillScore > tmpClipScore && tmpFillScore > tmpOrigScore){
				tmpScore = tmpFillScore;
				tmpServers = (ArrayList<Server>) tmpFillServers.clone();
			}else{
				tmpScore = tmpOrigScore;
				tmpServers = (ArrayList<Server>) servers.clone();
			}		
			
			
			if(tmpScore > resScore){
				
				resScore = tmpScore;
				resServers = (ArrayList<Server>) tmpServers.clone();
				predictFlavors = (LinkedList<Flavor>)predictFlavorsShuffledCopy.clone();//保存shuffle后的顺序
			}
			//如果分数更低,则以一定概率保存结果,防止优化陷入局部最优解
			else{
				
				if(Math.exp( (tmpScore-resScore)/T ) > Math.random()){
					
					resScore = tmpScore;
					resServers = (ArrayList<Server>) tmpServers.clone();
					predictFlavors = (LinkedList<Flavor>)predictFlavorsShuffledCopy.clone();
				}
			}	
		
			
	
														
			//一次分数更高,保存结果
				
																		
			T = r*T;
		}
		
	/**====================================  一次退火结束,比 较 ,裁剪    ====================================================================== */
		
		
		System.out.println("============================================================================");
		System.out.println("bestScore:"+ resScore);
		
		return resServers;					
	}
	public static int[] getClipServersEachFlavorNum(int flavorTypeNumber,int[] eachFlavorsNum, 
			int[] needPredictedFlavorType, ArrayList<Flavor> flavors){
		
		int[] sum = new int[flavorTypeNumber];
		for(int j=0;j<flavors.size();j++){		
			int x = Integer.parseInt(flavors.get(j).name.substring(6))-1;//Index returned, minimum is 0
			sum[x]++;			
		}

		int[] lastServerFlavorsNum = new int[needPredictedFlavorType.length];
		int[] clipServersFlavorsNum = new int[needPredictedFlavorType.length];
		for(int i=0;i<lastServerFlavorsNum.length;i++){
			
			int m = needPredictedFlavorType[i];//预测的type,代号
			int lastServerOneFlavorSum = sum[m];
			clipServersFlavorsNum[i] = eachFlavorsNum[i] - lastServerOneFlavorSum;
		}
		
		return clipServersFlavorsNum;	
	}
	/**
	 * 依次随机填入新的flavor,直到放不下为止
	 * @param flavorTypeNumber
	 * @param originalFlavorsNum 预测的flavvor数量
	 * @param servers	布置的服务器
	 * @param flavorCpuMemMap 
	 * @param needPredictFlavorsType
	 * @param CPUorMEM
	 * @return
	 */
	public static double fillServers(int flavorTypeNumber,int[] originalFlavorsNum, ArrayList<Server> servers,
			HashMap<String,Flavor> flavorCpuMemMap, int[] needPredictFlavorsType){
		double minT = 1;//终止温度
		double T = 20;//初始温度
		double r = 0.85;
		double originalScore = 0;
		double tmpScore = 0;
		double maxScore = 0;
		double originalR = 0;//原始总空间利用率
		double tmpR = 0;//当前总空间利用率
		ArrayList<Server> resultServers;
		ArrayList<Server> serversClone = (ArrayList<Server>) servers.clone();
		int[] tmpFlavorsSum = originalFlavorsNum.clone();
				
		originalR = getTotalCpuAndMemUsageRate(servers);		
		
		//最大分数初始是未添加任何flavor的布置得分
		originalScore = 1*originalR;
		maxScore = originalScore;
		Boolean stop = false;
		int i= servers.size()-1;//从最后一个server开始填充,填到第一个停止
		while(T>minT){
			
//			for(int i=servers.size();i>=0;i--){
//				
//			}
			stop = false;
			//随机添加一个类型flavor到servers的最后一个服务器中	
			int count = 0;
			while(true){
							
				int ran = (int) (Math.random()*needPredictFlavorsType.length);
				String key = "flavor"+ needPredictFlavorsType[ran];	
				Flavor ranflavor = flavorCpuMemMap.get(key);
				count++;
				//添加成功,退出
				if(servers.get(i).putFlavor(ranflavor)){				
					tmpFlavorsSum[ran]++;
					break;
				}
				//如果循环了多次找不到可以放置的flavor,退出
				if(count>flavorTypeNumber*1.5){
					stop = true;
					break;
				}
			}
			//当前添加失败,取前一个server,如果没有,则结束退火
			if(stop){
				if(i==0){
					break;
				}else{
					i--;
				}			
			}else{
				//添加成功,评价此操作
				//评价此次添加flavor的得分			
				tmpR = getTotalCpuAndMemUsageRate(servers);		
				
				tmpScore = getScore(originalFlavorsNum,tmpFlavorsSum,tmpR);
				//如果分数更高,则保存结果
				if(tmpScore > maxScore){
					
					maxScore = tmpScore;			
					resultServers = (ArrayList<Server>) servers.clone();
					
				}else{
					//如果分数更低,则以一定概率保存结果,防止优化陷入局部最优解
					if(Math.exp( (tmpScore-maxScore)/T ) >Math.random()){
						maxScore = tmpScore;			
						resultServers = (ArrayList<Server>) servers.clone();			
					}else{
						//否则不保存结果,且擦除对servers顺序的修改
						servers = (ArrayList<Server>) serversClone.clone();
					}
				}
			}
				
			
			T = r*T;		
		}
		return maxScore;
		
	}
	//获取服务器CPU和MEM总使用率
	public static double getTotalCpuAndMemUsageRate(ArrayList<Server> Servers){
		
		double rate = 0;
		int totalServersCpu = 0;
		int totalFlavorsCpu = 0;
		for(Server s:Servers){
			
			totalServersCpu += s.totalCpu;
			
			for(Flavor f:s.flavors){
				totalFlavorsCpu += f.cpu;
			}
		}
		int totalServersMem = 0;
		int totalFlavorsMem = 0;
		
		for(Server s:Servers){
			
			totalServersMem += s.totalMem;
			for(Flavor f:s.flavors){
				totalFlavorsMem += f.mem;
			}
		}
		
		
		rate = ( (double)totalFlavorsCpu/totalServersCpu + 
				(double)totalFlavorsMem/totalServersMem )/2;
		return rate;
	}
	//获取服务器CPU和MEM总使用率
	public static double getOneServerCpuAndMemUsageRate(Server Server){
		
		double rate = 0;
		
		int totalFlavorsCpu = 0;
	
				
		for(Flavor f:Server.flavors){
			totalFlavorsCpu += f.cpu;
		}
		
	
		int totalFlavorsMem = 0;											
		for(Flavor f:Server.flavors){
			totalFlavorsMem += f.mem;
		}
			
		rate = ( (double)totalFlavorsCpu/Server.totalCpu + 
				(double)totalFlavorsMem/Server.totalMem )/2;
		return rate;
	}
	
	/**
	 * 
	 * @param y 	虚拟机规格实际数量
	 * @param y1	虚拟机规格预测数量
	 * @param r		空间利用率
	 * @return
	 */
	public static double getScore(int[] y,int[] y1,double r){
		
		double score = 0;
		double a = 0;
		double b1 = 0;
		double b2 = 0;
		int N = y.length;
		
		//分子
		for(int i=0;i<N;i++){
			a += (y[i]-y1[i])*(y[i]-y1[i]);
		}
		a = (double)a/N;
		a = Math.sqrt(a);
		//分母
		for(int i=0;i<N;i++){
			b1 += y[i]*y[i];
			b2 += y1[i]*y1[i];
		}
		b1 = (double)b1/N;
		b2 = (double)b2/N;
		b1 = Math.sqrt(b1);
		b2 = Math.sqrt(b2);
		
		score = (1 - a/(b1+b2))*r;
		return score;
		
	}
	//double minServer = vecFlavors;
	public static void exchange(LinkedList<Flavor> list,int a,int b){
		Flavor tmp = list.get(a);
		list.set(a, list.get(b));
		list.set(b, tmp);
	}		
	
}

三、总结

最后终结在复赛220分,但是后来做中兴的比赛的时候,发现,当时退火的做的并不好,参数设置的有问题,导致退火的结果不稳定,如果参数改进,应该分配结果会更好吧,继续学习,加油吧。

感兴趣的可以下载我的华为软挑比赛代码,欢迎骚扰。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值