本文的主要工作是补充这篇博客的缺失代码,使之能够运行。

2018华为软挑--模拟退火+FF解决装箱问题【C++代码】_小马哥MAX的博客-CSDN博客算法简介:        装箱问题是一个NP完全问题,求解全局最优解有很多种方法:遗传算法、禁忌搜索算法、蚁群算法、模拟退火算法等等,本次使用模拟退火,它的优点是在参数合适的情况下基本上可以100%得到全局最优解,缺点是相较于其他算法,其稳定速度较慢。        如果你对退火的物理意义还是晕晕的,没关系我们还有更为简单的理解方式。想象一下如果我们现在有下面这样一个函数,现在想求函数的(全局)最...https://blog.csdn.net/majichen95/article/details/80023320?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165007496116780271530627%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=165007496116780271530627&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-80023320.nonecase&utm_term=%E6%A8%A1%E6%8B%9F%E9%80%80%E7%81%AB&spm=1018.2226.3001.4450

一、模拟退火算法介绍

装箱问题是一个NP完全问题,求解全局最优解有很多种方法:遗传算法、禁忌搜索算法、蚁群算法、模拟退火算法等等,本次使用模拟退火,它的优点是在参数合适的情况下基本上可以100%得到全局最优解,缺点是相较于其他算法,其稳定速度较慢。

如果你对退火的物理意义还是晕晕的,没关系我们还有更为简单的理解方式。想象一下如果我们现在有下面这样一个函数,现在想求函数的(全局)最优解。如果采用贪心策略,那么从A点开始试探,如果函数值继续减少,那么试探过程就会继续。而当到达点B时,显然我们的探求过程就结束了(因为无论朝哪个方向努力,结果只会越来越大)。最终我们只能找打一个局部最后解B。

可以看出 模拟退火其实也是一种贪心算法,但是它的搜索过程引入了随机因素。模拟退火算法以一定的概率来接受一个比当前解要差的解,因此有可能会跳出这个局部的最优解,达到全局的最优解。以上图为例,模拟退火算法在搜索到局部最优解B后,会以一定的概率接受向右继续移动。也许经过几次这样的不是局部最优的移动后会到达B 和C之间的峰点,于是就跳出了局部最小值B。

算法过程
        根据Metropolis准则,粒子在温度T时趋于平衡的概率为exp(-ΔE/(kT)),其中E为温度T时的内能,ΔE为其改变数,k为Boltzmann常数。Metropolis准则常表示为

Metropolis准则表明,在温度为T时,出现能量差为dE的降温的概率为P(dE),表示为:P(dE) = exp( dE/(kT) )。其中k是一个常数,exp表示自然指数,且dE<0。所以P和T正相关。这条公式就表示:温度越高,出现一次能量差为dE的降温的概率就越大;温度越低,则出现降温的概率就越小。又由于dE总是小于0(因为退火的过程是温度逐渐下降的过程),因此dE/kT < 0 ,所以P(dE)的函数取值范围是(0,1) 。随着温度T的降低,P(dE)会逐渐降低。

我们将一次向较差解的移动看做一次温度跳变过程,我们以概率P(dE)来接受这样的移动。也就是说,在用固体退火模拟组合优化问题,将内能E模拟为目标函数值 f,温度T演化成控制参数 t,即得到解组合优化问题的模拟退火演算法:由初始解 i 和控制参数初值 t 开始,对当前解重复“产生新解→计算目标函数差→接受或丢弃”的迭代,并逐步衰减 t 值,算法终止时的当前解即为所得近似最优解。

总结起来就是
        若f( Y(i+1) ) <= f( Y(i) )  (即移动后得到更优解),则总是接受该移动
        若f( Y(i+1) ) > f( Y(i) )  (即移动后的解比当前解要差),则以一定的概率接受移动,而且这个概率随着时间推移逐渐降低(逐渐降低才能趋向稳定)。相当于上图中,从B移向BC之间的小波峰时,每次右移(即接受一个更糟糕值)的概率在逐渐降低。如果这个坡特别长,那么很有可能最终我们并不会翻过这个坡。如果它不太长,这很有可能会翻过它,这取决于衰减 t 值的设定。

举个栗子

求函数f(x)=11*sin(6*x)+7*cos(5*x) , x∈[0,2*pi] 的最小值。

由函数图像可以看出函数fx的最小值是-17.833975,但存在很多极小值,要求全局最优可以采用模拟退火,具体代码如下:

//f(x)=11*sin(6*x)+7*cos(5*x),x∈[0,2*pi],求最小值,真实最小值为-17.833
#include <iostream>
#include <math.h>
#include <time.h>  #define pi 3.14159
#define num 30000 //迭代次数
double k = 0.01;
double r = 0.99; //用于控制降温的快慢
double T = 200; //系统的温度,系统初始应该要处于一个高温的状态
double T_min = 2;//温度的下限,若温度T达到T_min,则停止搜索  //返回指定范围内的随机浮点数  double rnd(double dbLow, double dbUpper)//产生(dbLow,dbUpper)之间的随机数
{double dbTemp = rand() / ((double)RAND_MAX + 1.0);return dbLow + dbTemp*(dbUpper - dbLow);
}double func(double x)//目标函数
{return 11 * sin(6 * x) + 7 * cos(5 * x);
}int main()
{double best = func(rnd(0.0, 2 * pi));double dE, current;int i;srand((unsigned)(time(NULL)));//用当前时间点初始化随机种子,防止每次运行的结果都相同while (T > T_min){for (i = 0; i < num; i++){current = func(rnd(0.0, 2 * pi));//产生新解dE = current - best;if (dE < 0) //表达移动后得到更优解,则总是接受移动  best = current;else{// 函数exp( dE/T )的取值范围是(0,1) ,dE/T越大,则exp( dE/T )也越大  if (exp(-dE / (T*k)) > rnd(0.0, 1.0))//有一定概率接受较差解best = current;}}T = r * T;//降温退火 ,0<r<1 。r越大,降温越慢;r越小,降温越快  }printf("最小值是 %f\n", best);return 0;
}

可以看出,模拟退火帮助我们得到了全局最优解。

二、装箱问题

有n个物品,体积为v1,v2,v3. . .然后要求用最少的箱子把这些物品里面,这个是基于贪心算法的思想。

装箱问题_lotluck的博客-CSDN博客_装箱问题

三、华为软挑完整代码

题目:简单来说,就是有n个内存大小一样、CPU核数一样的大型物理机,作为装箱问题中的“箱子”,而用户需要的虚拟机是装箱问题中的“物品”,虚拟机根据用户的需求其内存大小、CPU核数是各不相同的。求最少需要几个大型物理机。

代码的分段讲解见这篇博客:2018华为软挑--模拟退火+FF解决装箱问题【C++代码】_小马哥MAX的博客-CSDN博客

不过该博主隐藏了数据结构和主程序调用,并且在函数部分做了一些“手脚”,使得程序没法运行。补充并修改完毕后,完整代码如下:

#include <iostream>
#include <math.h>
#include <time.h>
#include <string>
using namespace std;int PsyPsNum = 0; //最优解,初始值无所谓,需要的物理机个数
int totalPreNum = 15; //总虚拟机typedef struct Flavor{string s;  //服务器名称 int PsId;  //分配给了编号为PsId的云服务器 int cpu; //cpu大小 int mem; //存储大小
}FlavorS; typedef struct GNode{    // 物品节点 int gnum;     // 挂在链上的编号 struct GNode *link;  //指向下一个物品节点
}GNode;typedef struct GBox{    // 物品节点 int remainder;     int mem;int box_no;struct GBox *next;  //指向下一个物品节点 GNode *head;  //
}GBox;int FlavorSBox(FlavorS goods[], int n, int cpu_num, int mem);void distribution(FlavorS flavors[])
{//PsyPsNum = FlavorSBox(flavors, totalPreNum, ECS.cpu, ECS.mem);PsyPsNum = FlavorSBox(flavors, totalPreNum, 56, 128);
}int FlavorSBox(FlavorS goods[], int n, int cpu_num, int mem) //装箱问题贪心算法
{int num = 0;GNode *pg, *t;GBox *hbox = NULL, *pb, *qb;int i;for (i = 0; i < n; i++) //遍历虚拟机信息数组{pg = (GNode *)malloc(sizeof(GNode)); ///分配货物节点单元//pg->s = goods[i].s;pg->link = NULL; //货物节点初始化if (!hbox)      //若一个物理服务器都没有{hbox = (GBox *)malloc(sizeof(GBox));hbox->remainder = cpu_num; //物理服务器可以容纳的CPUhbox->mem = mem;         //物理服务器可以容纳的内存hbox->head = NULL;hbox->next = NULL;num++; //物理服务器数量加1hbox->box_no = num;}qb = pb = hbox; //都指向物理服务器头while (pb)        //找物理服务器{if (pb->remainder >= goods[i].cpu && pb->mem >= goods[i].mem) //CPU能装下&&内存也能装下 break;                                                      //找到箱子,跳出whileelse{qb = pb;pb = pb->next; //qb是前驱}} //遍历物理服务器结束if (pb == NULL) //需要新物理服务器{pb = (GBox *)malloc(sizeof(GBox)); //分配物理服务器pb->head = NULL;pb->next = NULL;pb->remainder = cpu_num;pb->mem = mem;qb->next = pb; //前驱指上num++;         //物理服务器数量加1pb->box_no = num;}if (!pb->head) //如果物理服务器里没货{pb->head = pg;t = pb->head;goods[i].PsId = pb->box_no; //将虚拟机与物理服务器编号关联起来cout << goods[i].s << "装入" << goods[i].PsId << "物理服务器" << endl;}else{t = pb->head;while (t->link)t = t->link; //尾插t->link = pg;goods[i].PsId = pb->box_no; //将虚拟机与物理服务器编号关联起来cout << goods[i].s << "装入" << goods[i].PsId << "物理服务器" << endl;}pb->remainder -= goods[i].cpu;pb->mem -= goods[i].mem;}cout<<"需要服务器的个数"<<num<<endl; return num;
}//swapTimes表示交换次数,flavors表示需要交换的对象
void generateNew(int swapTimes, FlavorS flavors[])
{for (int i = 0; i < swapTimes; i++){int posx = rand() % totalPreNum;int posy = rand() % totalPreNum;swap(flavors[posx], flavors[posy]);}
}double rnd(double dbLow, double dbUpper)//产生(dbLow,dbUpper)之间的随机数
{double dbTemp = rand() / ((double)RAND_MAX + 1.0);return dbLow + dbTemp*(dbUpper - dbLow);
}void SimulatedFire(FlavorS flavors[])
{int best = PsyPsNum;cout << "before fire:" << PsyPsNum << endl;const int LL = 300;double k = 0.1;double r = 0.97;   //用于控制降温的快慢double T = 300;         //系统的温度,系统初始应该要处于一个高温的状态double T_min = 0.1; //温度的下限,若温度T达到T_min,则停止搜索//返回指定范围内的随机浮点数int dE, current;srand((unsigned)(time(NULL)));FlavorS *temp = new FlavorS[totalPreNum];for (int i = 0; i < totalPreNum; i++){temp[i].s = flavors[i].s;temp[i].cpu = flavors[i].cpu;temp[i].mem = flavors[i].mem;temp[i].PsId = flavors[i].PsId;}while (T > T_min){for (int i = 0; i < LL; i++){generateNew(50, temp);  //把15个箱子的顺序打乱 distribution(temp);     //然后,重新利用贪心得到最小值 current = PsyPsNum;dE = current - best;if (dE <= 0) //表达移动后得到更优解,则总是接受移动{best = current;for (int i = 0; i < totalPreNum; i++){flavors[i].s = temp[i].s;flavors[i].cpu = temp[i].cpu;flavors[i].mem = temp[i].mem;flavors[i].PsId = temp[i].PsId;}}  else{// 函数exp( dE/T )的取值范围是(0,1) ,dE/T越大,则exp( dE/T )也越大if (exp(-dE / (T * k)) > rnd(0.0, 1.0)){best = current;for (int i = 0; i < totalPreNum; i++){flavors[i].s = temp[i].s;flavors[i].cpu = temp[i].cpu;flavors[i].mem = temp[i].mem;flavors[i].PsId = temp[i].PsId;}}}}T = r * T; //降温退火 ,0<r<1 。r越大,降温越慢;r越小,降温越快}PsyPsNum = best;delete[] temp;cout << "after fire:" << PsyPsNum << endl;
}int main(){
//  int N;  //输入多少个服务器
//  scanf("%d",&N);
//
//  FlavorS flavors[N];
//  for(int i=0;i<N;i++){
//      scanf("%s %d %d",&flavors[i].s;&flavors[i].cpu,&flavors[i].mem);
//  } int N=15;FlavorS flavors[15];/*---------------内存的单位是mb---------------*/
//  flavors[0].s="flavor14"; flavors[0].cpu=16; flavors[0].mem=32768;
//  flavors[1].s="flavor11"; flavors[1].cpu=8; flavors[1].mem=16384;
//  flavors[2].s="flavor9"; flavors[2].cpu=4; flavors[2].mem=16384;
//  flavors[3].s="flavor8"; flavors[3].cpu=4; flavors[3].mem=8192;
//  flavors[4].s="flavor6"; flavors[4].cpu=2; flavors[4].mem=8192;
//  flavors[5].s="flavor5"; flavors[5].cpu=2; flavors[5].mem=4096;
//  flavors[6].s="flavor4"; flavors[6].cpu=2; flavors[6].mem=2048;
//  flavors[7].s="flavor3"; flavors[7].cpu=1; flavors[7].mem=4096;
//  flavors[8].s="flavor2"; flavors[8].cpu=1; flavors[8].mem=2048;
//  flavors[9].s="flavor1"; flavors[9].cpu=1; flavors[9].mem=1024;
//  flavors[10].s="flavor7"; flavors[10].cpu=4; flavors[10].mem=4096;
//  flavors[11].s="flavor10"; flavors[11].cpu=8; flavors[11].mem=8192;
//  flavors[12].s="flavor12"; flavors[12].cpu=8; flavors[12].mem=32768;
//  flavors[13].s="flavor13"; flavors[13].cpu=16; flavors[13].mem=16384;
//  flavors[14].s="flavor15"; flavors[14].cpu=16; flavors[14].mem=65536;/*---------------内存的单位是GB---------------*/flavors[0].s="flavor14"; flavors[0].cpu=16; flavors[0].mem=32768/1024;flavors[1].s="flavor11"; flavors[1].cpu=8; flavors[1].mem=16384/1024;flavors[2].s="flavor9"; flavors[2].cpu=56; flavors[2].mem=16384/1024;flavors[3].s="flavor8"; flavors[3].cpu=56; flavors[3].mem=8192/1024;flavors[4].s="flavor6"; flavors[4].cpu=56; flavors[4].mem=8192/1024;flavors[5].s="flavor5"; flavors[5].cpu=56; flavors[5].mem=4096/1024;flavors[6].s="flavor4"; flavors[6].cpu=56; flavors[6].mem=2048/1024;flavors[7].s="flavor3"; flavors[7].cpu=56; flavors[7].mem=4096/1024;flavors[8].s="flavor2"; flavors[8].cpu=56; flavors[8].mem=2048/1024;flavors[9].s="flavor1"; flavors[9].cpu=1; flavors[9].mem=1024/1024;flavors[10].s="flavor7"; flavors[10].cpu=4; flavors[10].mem=4096/1024;flavors[11].s="flavor10"; flavors[11].cpu=8; flavors[11].mem=8192/1024;flavors[12].s="flavor12"; flavors[12].cpu=8; flavors[12].mem=32768/1024;flavors[13].s="flavor13"; flavors[13].cpu=16; flavors[13].mem=16384/1024;flavors[14].s="flavor15"; flavors[14].cpu=16; flavors[14].mem=65536/1024;distribution(flavors);  //得到贪心算法的最优解,放入PsyPsNum中 SimulatedFire(flavors); return 0;
}

要想理解这个程序的输入和输出必须找华为软挑初赛的原题看一下!

跑完这个大概需要1~2分钟时间,耐心等待。

【C++】2018华为软挑:模拟退火+贪心FF解决装箱问题相关推荐

  1. 2018华为软挑--模拟退火+FF解决装箱问题【C++代码】

    算法简介: 装箱问题是一个NP完全问题,求解全局最优解有很多种方法:遗传算法.禁忌搜索算法.蚁群算法.模拟退火算法等等,本次使用模拟退火,它的优点是在参数合适的情况下基本上可以100%得到全局最优解, ...

  2. O准备如何苟进复赛圈?华为软挑开挂指南(附赛题预测)

    这篇文章纯粹是心血来潮,原因是去年上传到github的参赛代码,前几天又有两个人star和fork了. 记得star热潮还是去年4月复赛刚结束的那几天,厚着脸皮在八大赛区的群里发消息求关注,之后就陷入 ...

  3. 华为软挑赛2023-初赛笔记

    前言 比赛介绍 官方链接: 2023华为软件精英挑战赛--普朗克计划 (huaweicloud.com) 赛题介绍 场景介绍 官方赛题介绍: 2023华为软件精英挑战赛初赛赛题及相关材料发布_2023 ...

  4. 2022华为软挑比赛(初赛笔记)

    文章目录 2022华为软挑(初赛笔记) 1. 赛题要求 2. 解决方案 2.1 挑选适合的边缘节点 2.2 第一轮:最大分配 2.3 第二轮:均值分配 总结 本文仓库地址: Github-CodeCr ...

  5. 2020华为软挑热身赛代码开源-思路大起底(华为软件精英挑战赛编程闯关)

    本文首发于个人公众号[两猿社],后台回复[华为],获取完整开源代码链接. 昵称:lou_shang_shi_bian_tai 成绩:0.032 社长没有针对硬件做任何优化,热身赛成绩也一般.但有些比赛 ...

  6. 2021华为软挑部分答疑——哪些你有错却总是找不到的地方,我来带你找啦(含标准输入代码)

    前期工作: 2021华为软挑初探--代码实现 2021华为软挑再探--代码实现 1 关于打包 在windows系统下,先把你写的程序写在src里面的CodeCraft-2021里面 然后在这个页面,将 ...

  7. 华为软挑赛2023-复赛笔记

    前言 比赛介绍 参考初赛博客: 华为软挑赛2023-初赛笔记_没有梦想的大白兔的博客-CSDN博客 赛题变动 官网赛题: 华为云论坛_云计算论坛_开发者论坛_技术论坛-华为云 (huaweicloud ...

  8. 2020华为软挑热身赛-这些坑我帮你踩过了(华为软件精英挑战赛编程闯关)

    本文始发于个人公众号[两猿社]. 声明,为保证比赛公平,本文不会提供比赛源码,仅提供思路与踩坑经验. 他来了,他来了,他带着面试绿卡走来了. 他来了,他来了,他带着20w大奖走来了. 一年一度的华为软 ...

  9. 2019华为软挑代码总结-交通规则模拟

    文章目录 前言 三.数据读入后怎么跑呢? 总结 四.怎么看效果? 前言 以前一份代码的阅读,2019华为软挑 三.数据读入后怎么跑呢? 思路: 1.创建地图类,将地图中每个路口到其他路口最短路径保存. ...

  10. 装箱问题c语言函数,C语言基于贪心算法解决装箱问题的方法

    本文实例讲述了C语言基于贪心算法解决装箱问题的方法.分享给大家供大家参考,具体如下: 问题描述: 有一些箱子,容量为V,同时有n个物品,每个物品有一个体积(小于等于箱子容量),要求将物品全部装入箱子中 ...

最新文章

  1. Android自定义控件前导基础知识学习(一)——Canvas
  2. Android存储路径你了解多少?
  3. 趣学python3(47)--尾递归
  4. 练习11.1 奥运五环色 7-1 藏头诗
  5. SQL技巧(多行合并一列)
  6. linux鼠标移动消失,debian6升级后鼠标指针消失
  7. python金融股票市场数据分析神器TA-Lib学习使用
  8. ai 实用新型专利_专利制度协调AI创造的创新
  9. stm32f407zg跟ze的区别_STM32F103ZE和STM32F207ZG的芯片区别
  10. nx零件库插件_3DSource企业自定义零件库插件
  11. MATLAB加入螺旋相位板调制,螺旋相位板原理及使用手册
  12. 如何用视频转换器把qlv格式转换mp4
  13. Linux efi分区被删除了,如何恢复意外删掉的EFI系统分区
  14. Therefore, hence, so, then, thus
  15. 汽车充电桩的功能有哪些
  16. 2-18-python基础学习-0409
  17. 使用Java编写《拳皇97》,致敬经典,还原八神庵大战草稚京
  18. 金蝶EAS,凭证接口,生成凭证,删除凭证,WebService接口
  19. 云his系统源码 SaaS应用 基于Angular+Nginx+Java+Spring开发
  20. 软件测试人员的职业发展之路——写给还在迷茫的测试人

热门文章

  1. [python3] zipfile压缩目录下所有的文档都被压缩,并解决压缩路径过深的问题
  2. JAVA实用教程(第2版)配套源码笔记
  3. 万年历单片机课程设计百度文库_单片机课程设计-万年历
  4. Delphi入门教程
  5. python 爬虫抓取网页数据导出excel_如何用excel实现网页爬虫
  6. 如何安装SQL2005 EXPRESS版
  7. Ubuntu 20.04 锐捷客户端安装记录
  8. 64位电脑c语言程序下载,vc2010 x64位
  9. 成功解决:loadrunner提示计算机上缺少vc2005_sp1_with_atl_fix_redist
  10. 100个常用的 JS 代码