2021华为软件精英挑战赛(粤港澳赛区复赛第八)

article/2025/3/3 18:03:44

一、序言

        总结一下四月份参加的华为软挑赛,距离现在已经结束了四个多月,终于有时间抽空写写总结了(小作文),我们是粤港澳赛区的620&619-F3队,第一次参加这次比赛,本想尝试一下,但没想到也挤进了复赛,而且还拿到粤港澳赛区复赛第八的成绩。BTW,我们实验室就有三队进复赛了,另外两队分别是少吃零食多睡觉队和对队,两队都是大佬级人物orz,对队还进了总决赛(膜.
在这里插入图片描述

二、思路

        这个比赛无非就是三个优化点,①购买策略②迁移策略③部署策略
我们算法的具体流程如下:
在这里插入图片描述
        具体算法用C++语言编写,用到的数据结构如下:
在这里插入图片描述

三、初赛篇

Vesion1

①购买策略

        一开始的思路是使用贪心算法,这个也是大多数队伍想到的idea。具体做法是:对服务器的硬件成本从小到大排序,然后在部署服务器的时候,若发现没有服务器能放进虚拟机的情况下,去购买最便宜的服务器。这使得我们的成本从14e直降到11.8e。但是采取此策略后,当我们输出服务器的型号,发现几乎都是价格最便宜的前三台,其他服务器没有用过。这也是成本一直降不下的原因,因为没有合理的把其他服务器也利用上。

②部署策略

        既然购买策略是贪心算法,那么部署策略也很自然的想到用贪心算法,具体做法是:对服务器每天的占用情况从大到小排序,所以在部署虚拟机到服务器的时候,优先把虚拟机布置在占用情况最大的服务器,目的是优先填满服务器的资源,充分利用服务器资源。

③迁移策略

        版本1暂不考虑迁移策略,我们的想法是先把购买和部署策略做的最优或者较优的情况下才考虑迁移策略。

Vesion2

①购买策略

我们围绕购买策略进行了更深一步的思考,我们想到如下排序的策略:
        ①服务器每日的耗能成本从小到大排序
        ②服务器的硬件成本/(cpu+内存)即考虑服务器的性价比从高到低
        ③服务器的CPU*0.75+服务器的内存*0.25(传说中的魔法数字)从小到大
,但很可惜,这些策略都没有我们以前的策略成本低,即对硬件成本简单粗暴的从小到大排,然后买最便宜的服务器。遂考虑怎么进一步优化部署策略

②部署策略

        依然是围绕贪心算法去优化排序函数,这里使用了上述的策略3,也就是传说中的魔法数字,服务器的CPU*0.75+服务器的内存*0.25。有奇效!!也就是在部署虚拟机到服务器的时候,优先把虚拟机布置在策略3从小到大排序后的服务器中。这使得我们的成本下降了,从11.8e降到11.5e。 这里提一个坑:我们每次在布置虚拟机的时候,都对服务器进行排序sort,发现我们的时间一直会显示超时或者接近超时,后面经过进一步优化,我们尝试过把“每一条虚拟机请求后排序服务器” 改成 “每一天处理所有虚拟机请求前排序服务器”,但是这么做之后,时间的确减少了,但是成本提高了呀,我去…权衡再三,还是成本优先。到最后,我们采取了另一种方法:遍历服务器,也就是每次部署虚拟机到服务器的时候,我们不对服务器进行排序。而是遍历一遍当前存在的全部服务器,去找到占用资源最大的服务器,这个策略使得运行时间大大减小,这也使得我们算法能在迁移策略腾点时间,因为迁移策略真的太耗时了。

③迁移策略

        这个版本开始考虑迁移策略,很自然的,跟大多数的队伍想法一样,我们也是优先把虚拟机迁移到占用资源大的服务器,即尽可能的使服务器的剩余资源小,由于前面版本没有加迁移,所以加了迁移策略,成本理所当然的也下降了,从11.5e降到11.249e,虽然成本降了,但是运行时间增长的异常夸张,直接激增到100多秒甚至超时(大赛限制在180s)。原因是我们在迁移策略写了一个五层循环!注意是五层循环!注意是五层循环!重要的事情说三遍!后期优化到了三层循环,时间才降低了,但还是逼近超时。

Vesion3(初赛接近尾声)

①购买策略不变

②部署策略不变

③迁移策略(二分+小暴力)

        迁移策略我们队长想到了一个特别骚的套路,前无古人后无来者,因为我们之前的迁移策略,包括很多队伍的策略都是纯暴力,也就是不断去遍历服务器,导致时间大幅增加。于是我们想起了大学算法分析课上的二分查找(就记得这个了hhh),即在迁移时,用二分查找方法找到第一个能放入该虚拟机的服务器,然后在该虚拟机附近进行一个小范围的暴力搜索,也就是大大减小了暴力搜索耗费的时间,且成本也进一步下降了!

        这里补充一点:我们在处理每天的请求时,先把当天的连续虚拟机add请求(遇到del请求前)全部push进一个vector,然后当遇到del请求后,对刚才的vector进行排序,按照scores(虚拟机的CPU+内存)从大到小排序,然后先处理scores大的虚拟机add请求,再处理scores小的虚拟机add请求;依次类推,继续下一个新的add请求vector组,遇到del请求后,开始处理add请求vector组…,使用此方法,成本也有显著的下降。

初赛结束

这里补充一个小插曲:
        在初赛前几天,我们排名还在24,而32强才能进复赛,所以我们还是不能松懈。因为后面一堆老卷王,指不定在初赛的最后这几天,把我们一举反超了,所以我们一点都不敢掉以轻心。事实证明,我们当时居安思危是十分正确的,因为我们在最后一刻排到了33名(电影都不敢这么拍),然后我们那天心灰意冷的去吃了学校外面的鸡公煲,路上一直在叹息,觉得遇到了人生的大挫折,特别咽不下那口气。但是戏剧性的一幕来了,比赛结束后的第二天下午,主办方公布名单的时候,我们队伍居然也挤进去初赛了(电影都不敢这么拍)。然后仔细询问主办方为何我们排名还靠前了(当时是从33名升到了28名),主办方说因为前排有不少人的代码被查重系统拦截了,所以取消成绩。哇…得知这个消息后,欣喜若狂…感觉经历了人生的一个大起大落!

四、复赛篇

Vesion1

①购买策略

        直接交我们的初赛baseline,成本为21e,由此可见,复赛的数据集比初赛复杂,且数据规模更大。从这个版本开始,我们的购买策略变动较大,我们添加了滑动窗口,并综合考虑了如下因素:
        ①服务器的CPU/内存尽量接近虚拟机的CPU/内存
        ②服务器的CPU/内存尽量接近滑动窗口当天所需要的CPU/内存
        ③服务器自身CPU尽量接近内存
        ④服务器的硬件成本尽量接近当前的平均硬件成本
        ⑤服务器的每日耗费成本尽量接近当前的平均耗费成本
且④⑤点引入了日期的概念,即前期选择服务器硬件成本大的、能源成本小的;后期选择服务器硬件成本小的、能源成本大的。
具体代码如下:

for (auto it = svrInfos.begin(); it != svrInfos.end(); it++){if (overLoad(createVmInfo, it->second) == 1)//如果超出负载,则跳过continue;double totalCpu = it->second.cpuCores_A * 2;//服务器的CPU核数double totalMemory = it->second.memorySize_A * 2;//服务器的内存数double svrRate = totalCpu / totalMemory;//若大于1,则代表服务器的CPU>Memory;若小于1,则代表服务器的CPU<Memorydouble cpuMemoryBalance = abs(totalCpu - totalMemory) / (totalCpu + totalMemory) / 2;//尽可能使服务器的CPU与Memory相近double svrCostRate = it->second.serverCost / AvgSvrCost;//使svrCost尽可能接近1double svrPowerCostRate = it->second.powerCost / AvgPowerCost;//使powerCost尽可能接近1if (slideWindowInfo.needCpu > 0 && slideWindowInfo.needMemory > 0 && slideWindowInfo.totalVmsNum > 0)//当天del数小于add数的情况{double windowRate = slideWindowInfo.needCpu * 1.0 / slideWindowInfo.needMemory;//若大于1,则代表滑动窗口的CPU>Memory;若小于1,则代表滑动窗口的CPU<Memorydouble nodeRate = slideWindowInfo.twoNodesNum * 1.0 / slideWindowInfo.totalVmsNum;//代表双结点数loss = abs(svrRate - vmRate) * svrRate_vmRate_coef +//尽可能使虚拟机的Rate与服务器的Rate相近abs(windowRate - svrRate) * windowRate_svrRate_coef +//尽可能使当天的Rate与服务器的Rate相近cpuMemoryBalance * nodeRate * cpuMemoryBalance_coef +//若双结点比较多,则cpuMemoryBalance的值要求小;若单结点比较多,则cpuMemoryBalance的值可大可小svrCostRate * start / TotalDay * svrCost_coef +//前期选择服务器硬件成本大的,后期选择服务器硬件成本小的svrPowerCostRate * (TotalDay - start) / TotalDay * svrPowerCost_coef;//前期选择服务器能源成本小的,后期选择服务器能源成本大的}}

②部署策略

        我们的部署策略为每次部署虚拟机在服务器时,遍历所有服务器,计算loss,loss的定义为若分配此虚拟机后服务器的剩余CPU*0.75+内存*0.25;然后找出当中最小loss的服务器,然后部署虚拟机在服务器上。核心思想也是尽可能让服务器的剩余资源小。

③迁移策略

        与初赛version1一样,暂不考虑迁移策略。

        这里补充一点:也就是我们上面提到的滑动窗口,因为复赛要求从初赛的给出全部T天的数据变更为只给出前K天的数据,当你的程序完整输出了第一天的决策信息后,你将会读取到第 K+1 天的请求序列数据。然后你需要输出第二天的决策信息,并且读取第 K+2 天的请求序列,以此类推。因此,我们考虑到使用滑动窗口,也就是在一开始统计前K天的虚拟机所需的全部CPU和内存(当中如果有虚拟机释放的话,则对应的CPU和内存也要减去),然后给出第K+1、K+2…天后,依次计算对应的滑动窗口大小。

Vesion2–Final-version

①购买策略不变

②部署策略

        在计算原来的loss基础上,添加了一个负载均衡的判断,若部署该虚拟机到服务器时,A/B结点的剩余资源出现一个特别大或者一个特别小的情况时,loss+=1e5,也就是loss会特别大,即算法会尽量不满足这种极端情况的出现。

③迁移策略

         我们发现迁移策略的思想可以从部署策略中借鉴,也就是同样都是计算loss,遍历所有服务器,loss的定义为若分配此虚拟机后服务器的剩余CPU*0.75+内存*0.25;然后找出当中最小loss的服务器;然后迁移虚拟机到最小loss的服务器上。核心思想也是与部署策略一样,尽可能让服务器的剩余资源小。这里有个细节就是,我们添加了一个参数:使用率rate,由于比赛有限制运行时间,所以我们规定,使用率<0.93的服务器才进行迁移。这里再补充一个trick:我们原本的运行时间是达到了150秒左右,但是使用了C++的引用特性后(也就是在函数形参前加&引用),即从值传递变为引用传递,大大减少了函数的拷贝成本,运行时间直降100秒,于是我们才能把腾出之后的时间全部用于迁移策略,所以使用率rate才能设置到0.93这么高。

复赛当天

主办方在复赛当天变更了需求,如下:
在这里插入图片描述
        我们在复赛当天主要就是两个人负责调参,一个人负责处理这个变更的需求点。这个变更的需求点,我们尝试过在vmnum,也就是虚拟机数目大于15000时,进行大规模迁移,即尝试迁移所有的虚拟机,但成本只是略微下降了。此次复赛最关键的就是参数问题,队长在最后一刻把一些冗余的参数全部delete,最终竟然从第12名升到第8名。这也给了我们一个十分重要的启示:就是 在设计算法时,尽量减少使用硬参数的情况,若参数过多,极易造成过拟合的情况,导致算法遇到新的数据集效果变差;还有一点就是,我们的baseline版本过多,到后期,baseline版本多达几十个,版本管理混乱,下次要考虑使用git来做版本控制;另外,在复赛当天参加现场比赛时,我们应该提前准备好几个版本的算法,而不是指望唯一一个最优的版本,把较优的版本都可以尝试提交上去,或许有奇效!

五、总结与反思

        从初赛的第33名到复赛的第8名,用了一个多月的准备时间,这是最有收获的一次比赛!一路走来,有经历过深夜改bug改到头秃的绝望,也有体会到排行榜名次上升的喜悦。不过不管结果怎么样,享受的是整个比赛的过程!特别幸运能在比赛中结识到各位大佬!

六、附录

        发一下当天复赛的图来纪念一下哈哈哈:
在这里插入图片描述

Github开源地址:https://github.com/VirgilG72/CodeCraft-2021


http://chatgpt.dhexx.cn/article/soYsTb4Z.shtml

相关文章

css 给文字加下划线

css给文字加下划线 直接贴代码 span {cursor: pointer;&:hover {color: #40A9FF;text-decoration: underline;}}

Excel批量设置下划线

Excel批量设置下划线 目录 Excel批量设置下划线 1、框选需要设置的单元格内容&#xff0c;鼠标右键选择“设置单元格格式” 2、点击“自定义”在类型框中输入“ *_ ” 点击“确定”自动生成&#xff08;注意这个 *_符号需要将输入法切换为英文输入法&#xff09; 3、完成…

h5下划线怎么设置_怎么给文加下划线?

怎么给文本加下划线&#xff1f;下面本篇文章就给大家介绍一下HTML页面和word文档中给文本加下划线的方法。有一定的参考价值&#xff0c;有需要的朋友可以参考一下&#xff0c;希望对大家有所帮助。 HTML页面中给文本添加下划线 在HTML页面中怎么给文本添加下划线&#xff1f;…

latex输出下划线

第一种&#xff1a; 如果只是在作者的邮箱...输出下划线的话直接使用 \_ 就可以了 ma\_pengsen 输出结果&#xff1a; 第二种&#xff1a; 如果要在下划线上输出东西&#xff0c;那需要 \underline{XXXXXXX} ma\underline{ABCDEFG} 结果&#xff1a;

speedoffice(Word)文字怎么添加下划线

Word里面编辑文字&#xff0c;有时需要添加下划线&#xff0c;那么怎么添加下划线了&#xff1f;以最常用的speedoffice为列。 1、首先&#xff0c;我们用speedoffice打开Word文件&#xff0c;选中需要添加下划线的文字内容&#xff1b; 2、然后&#xff0c;鼠标点击选择“主页…

css里给文字加下划线代码,css给文字加下划线

语法&#xff1a;linear-gradient(direction, color-stop 1, color-stop 2,……) 简单用法&#xff1a;background-image: linear-gradient(red, transparent); 增加角度&#xff0c;linear-gradient(45deg, red, transparent) 加个position&#xff1a;linear-gradient(45deg,…

Word调整文字和下划线的间隔

工作环境(蓝色粗体字为特别注意内容) 1&#xff0c;开发环境&#xff1a;Microsoft word 2007 2&#xff0c;参考文献&#xff1a;https://blog.csdn.net/yiluyangguang1234/article/details/50158381 我们在使用Word编辑文档的时候&#xff0c;遇到有的标题带下划线的&#…

CSS设置下划线与文字间距距离

css的写下划线 text-decoration: underline; 但是这样的样式下划线太靠近文字了 如图 修改方式 border-bottom: 1px solid red;padding-bottom: 8px; 如图

word下划线,间距调大方式

都是下划线&#xff0c;是不是第一个看着更舒服&#xff0c;行间距更大。 下面的就是&#xff1a;我们常用的方式&#xff0c;ctrlu&#xff0c;下划线。 上面的是&#xff1a;连续打一行-&#xff0c;就是------&#xff0c;然后换到下一行&#xff0c;enter时候就出现了一条…

css里给文字加下划线代码,css添加文字下划线样式的方法

css添加文字下划线样式的方法 发布时间&#xff1a;2020-08-31 13:54:27 来源&#xff1a;亿速云 阅读&#xff1a;65 作者&#xff1a;小新 这篇文章将为大家详细讲解有关css添加文字下划线样式的方法&#xff0c;小编觉得挺实用的&#xff0c;因此分享给大家做个参考&#xf…

关于HTML,CSS中某一文字的下划线长度的改变

本人 刚刚入坑前端几个月。对于很多问题看的不是恨透测。前几日&#xff0c;写一个学长交代的小小网页时&#xff0c;遇到了一个问题。前前后后变化了好多次。最后解决了。我想有一些其他的学习者也有可能会遇到这类问题&#xff0c;特分享出来。希望可以帮到大家。 当时中需要…

word下滑线设置

方法一&#xff1a; 选择“文本”——>“选项”——>"高级"——>"为尾部空格添加下划线" step1: step 2: step3: step 4&#xff1a; 方法二&#xff1a; 选中需要添加下划线的部分——>"右键段落“——>"中文版式”——>“…

【CSS】下划线与文字间距,下划线粗细以及下划线颜色的设置

最开始的时候了解下划线的属性是&#xff1a; text-decoration:underline;但是&#xff0c;很遗憾的是&#xff0c;对于设计做的下划线用浏览器默认属性样式很难调整&#xff0c;使用这个属性并不能调整下划线与文字的间距&#xff0c;而且对于下划线的颜色也不好调整&#xf…

Python中函数的返回值

在之前的博客中分别介绍了Python中函数的定义、调用、以及函数的参数等&#xff0c;但是大家有没有发现&#xff1a;我们创建的函数都只是为我们做一些事&#xff0c;做完了就结束。但实际上&#xff0c;有时还需要对事情的结果进行获取。这类似于主管向下级职员下达命令&#…

python return_python 如何获得返回值 return

展开全部 前面两位的方法2113其实和先初始化AA&#xff0c;在调用AA的test()效果是一样5261的&#xff0c;在初4102始化AA()的时候&#xff0c;调用的那次test()的返回1653值已经丢了&#xff0c;比如这样定义&#xff1a;class AA(): def __init__(self): self.count0 sel…

python 自定义函数的返回值

python中自定义的函数&#xff0c;有的有返回值&#xff0c;有的没有返回值&#xff0c;且返回值的类型也需注意。 1、无返回值 例如 list 的 append 操作就是无返回值的&#xff0c;换句话说就是不能进行如下的连续操作&#xff1a; list [] list.append(1).append(2) 2、返…

python中函数的返回值,你了解吗?

函数返回值 1. “返回值”介绍 现实生活中的场景: 我给儿子10块钱&#xff0c;让他给我买包烟。这个例子中&#xff0c;10块钱是我给儿子的&#xff0c;就相当于调用函数时传递到参数&#xff0c;让儿子买烟这个事情最终的目标是&#xff0c;让他把烟给你带回来然后给你对么…

python报错怎么返回_python函数怎么返回值

python函数使用return语句返回“返回值”&#xff0c;可以将其赋给其它变量作其它的用处。所有函数都有返回值&#xff0c;如果没有return语句&#xff0c;会隐式地调用return None作为返回值。 python 函数使用 return 语句返回 "返回值"&#xff0c;可以将其赋给其…

08、Python函数的返回值

函数返回值的特性 首先&#xff0c;让我们先来看两个例子 #示例一 def exa_a(x):print(x)return x1def exa_b(x):return x1print(x) #这句会执行吗&#xff1f;if __name__ __main__:print(exa_a(2))执行上面的语句我们可以得出return的第一个特性&#xff1a; 特性一&#…

说说 Python 的函数返回值

函数可以处理一批数据&#xff0c; 然后返回一个或一组值&#xff0c;这些值被称为函数的返回值。 在函数内部可以使用 return 语句将值返回给调用函数的代码行。 通过使用返回值&#xff0c;我们可以将程序中的大部分复杂或可复用的工作放到函数中去完成&#xff0c; 从而简化…