目录
先看看题目,华为软挑题目详情
一、初赛
先解析输入文件,按照他要求格式输出文件。预测数据且分配只考虑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变为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分,但是后来做中兴的比赛的时候,发现,当时退火的做的并不好,参数设置的有问题,导致退火的结果不稳定,如果参数改进,应该分配结果会更好吧,继续学习,加油吧。
感兴趣的可以下载我的华为软挑比赛代码,欢迎骚扰。