2021华为软挑赛题_思路分析——实时更新,做多少更多少(二)
文章目录
本专栏食用建议
- 随着文章越更越多,本专栏的内容到底该如何食用,笔者在此做出如下建议,望能为参赛的诸位节省宝贵的时间和精力:
前言
- 感谢各位朋友的支持与赞同,空口无凭,奋笔开更~
3月13日更新
四、购买、分配方法的伪代码实现与删除请求、调度算法的阐述
续接上文(三、换一种解题思路),此处,力在将上文中提到的算法,以伪代码的形式进行一定的实现,达到算法落地的目的。同时,将之前为考虑的删除请求、调度算法的思路进行一定的阐述。
Ps:此算法是舍性能、求最优,以最为直观的逻辑结构加以呈现,若有更好的方法,欢迎评论区分享~
1. 购买、分配方法的伪代码实现:
1.1 解题相关前提定义:
- 服务器总表 = (host型号, hostCPU核数, host内存大小, 硬件成本, 每日能耗成本,性价比, host核内比)
- 虚拟机总表 = (vm型号, vmCPU核数, vm内存大小, 是否双节点部署,vm核内比)
- 一日请求总表 = (vm型号, 虚拟机 ID, vmCPU核数, vm内存大小, 是否双节点部署, vm核内比)
Ps:接下来将以training-data\training-1.txt中的第一天的请求数据为例加以说明。
- 服务器总表:80条数据,每一条代表一款可以购买的服务器;
- 虚拟机总表:800条数据,每一条代表一款可供用户购买的虚拟机;
- 一日请求总表:142条数据,数据与“虚拟机总表”匹配得来,每一条数据代表一个用户购买了一台虚拟机的请求。
- 核内比:服务器(or虚拟机)Cpu核数与内存容量的比值。
- 相关数据集定义
- hostListAll = (hostType, hostCpuNum, hostMemSize, hostHardWareCost, hostDayCost, hostXingJiaBi, hostCoreMemBi)#服务器总表
- vmListAll = (vmType, vmCpuNum, vmMemSize, vmDoubleOrNot, vmCoreMemBi)#虚拟机总表
- addOneDayListAll = (vmType, vmId, vmCpuNum, vmMemSize, vmDoubleOrNot, vmCoreMemBi) #一日请求总表(与vmListAll匹配后所得,只包含add请求)
- 思路复述:
利用凑数的方法,使用一日请求总表中的vmCPU核数、vm内存大小凑出服务器总表中每一款服务器的hostCPU核数、host内存大小(尽可能接近的凑出),计算凑出的结果与hostCPU核数、host内存大小之间的差值,取80款服务器中差值最小且排列靠上(服务器总表按性价比升序排列过,性价比数值越小,性价比越高,详见上文)的服务器进行购买。并且在凑数的过程中,每一组凑数的策略即为虚拟机的分配策略。
1.2 更新思路:
- 服务器总表现在不仅按照性价比进行了升序排序,同时服务器的数据也按照核内比进行了分类,核内比大于1的分成一类,小于1的分成一类。
- 下面算法描述时,已经自动默认,核内比大于1的请求去凑核内比大于1的服务器;核内比小于1的请求去凑核内比小于1的服务器。
Ps:若此处想不通,请耐住寂寞继续往后看,会豁然开朗的~
- 首先,将addOneDayListAll按照vmDoubleOrNot(是否双节点部署)进行分类,便于实现购买与分配单双节点虚拟机到不同服务器上。分类后的数据分别放在addOneDayListDouble(双节点请求), addOneDayListSingle(单节点请求)。
- 其次,依据分类后的两个列表中的全部请求,计算出请求中虚拟机的averageVmCoreMemBi (平均核内比),并判断其值是否大于1;若大于1,将两个列表按照vmCpuNum(核数)将vm升序排列;否则,按照vmMemSize(内存容量)升序排列(原因在后面解释)。
- 然后,将分类好的数据执行凑数步骤(以addOneDayListDouble(双节点请求)凑出第一款类型的服务器为例):
- 若其averageVmCoreMemBi(平均核内比) > 1,for遍历addOneDayListDouble中每个虚拟机的vmCpuNum(核数),并不断累加其vmCpuNum(核数)同时判断这个数值sumVmCpuNum与第1款服务器(假设叫做host_1)的hostCpuNum进行比较;若hostCpuNum > sumVmCpuNum,则持续累加sumVmCpuNum;否则,break跳出循环。当然,这个过程也会记录凑出该款服务器的全部请求存成列表host_1_DistributionList。
- 计算每一款服务器,凑出的结果与服务器自身hostCPU核数、host内存大小之间的差值,取全部款服务器中差值最小且排列靠前的服务器购买。并将其对应的host_1_DistributionList中的请求在原表addOneDayListDouble中删除。
- 解释:之所以计算averageVmCoreMemBi ,是为了判断当前这一组请求中,核的数值更大还是内存容量的数值更大。当核心数比内存容量更大时,不断累加sumVmCpuNum至不能再增加时,此时全部请求的vmMemSize的和一定小于hostMemSize,这就保证了如此得来的购买分配方案可以采纳。
- Ps:数据已经提前按照核或者内存升序排序过。
- 最后,直至将addOneDayListDouble(双节点请求)与addOneDayListSingle(单节点请求)两个列表中的请求都清空,算法结束。返回得到的所需购买的服务器型号与数目,以及对应的虚拟机分配策略。
1.3 伪代码实现(没写完,今天肝不动了,来日补上):
#相关数据集定义
hostListAll = (hostType, hostCpuNum, hostMemSize, hostHardWareCost, hostDayCost, hostXingJiaBi, hostCoreMemBi)#服务器总表
vmListAll = (vmType, vmCpuNum, vmMemSize, vmDoubleOrNot, vmCoreMemBi)#虚拟机总表
addOneDayListAll = (vmType, vmId, vmCpuNum, vmMemSize, vmDoubleOrNot, vmCoreMemBi) #一日请求总表(与vmListAll匹配后所得,只包含add请求)
#将oneDayListAll按照vmDoubleOrNot进行分类,拆成单、双节点两个列表
def DoubleOrSingle(addOneDayListAll):
i = 0 #临时变量
j = 0 #临时变量
for addOneDayList in addOneDayListAll:
if addOneDayList[4] == 1:
addOneDayListDouble[i+=1] = addOneDayList
else:
addOneDayListSingle[j+=1] = addOneDayList
return addOneDayListDouble, addOneDayListSingle#返回两个列表
addOneDayListDouble, addOneDayListSingle= DoubleOrSingle(addOneDayListAll)
#依据核内比,将hostListAll与oneDayListAll按照核内比是否大于一分成两类
#购买分配方法
def BuyingAndDistribution(hostListAll, requireList):
#依据请求表中的当日请求,计算vm的平均核内比averageVmCoreMemBi ,并判断其值是否大于1;若大于1,将oneDayListAll按照vmCpuNum(核数)将vm升序排列;反之,按照vmMemSize(内存容量)升序排列
sumVmCoreMemBi = 0
for oneDayList in requireList:
sumVmCoreMemBi= oneDayList[5] + sumVmCoreMemBi
averageVmCoreMemBi = sumVmCoreMemBi / len(oneDayListAll)
if averageVmCoreMemBi > 1:
#按照vmCpuNum(核数)将vm升序排列
else:
#按照vmMemSize(内存容量)升序排列
2. 删除请求、调度算法的阐述:
2.1 分析删除请求与调度算法:
- 删除请求:
- 首先,删除请求一定要在每日处理add请求前进行处理。
- 其次,删除请求以vmId为其唯一索引,因此在将虚拟机分配到不同服务器时,需要保存该虚拟机的vmId。
- 最后,每次删除操作都会使服务器资源(核、内存)增加,因此此处需要调度。当然也有可能使一台服务器变空,变空后服务器会停机,切记。 - 调度算法:
- 首先,虚拟机之所以需要调度,主要目的是为了清除由分配与删除时产生的资源空隙。
- 其次,个人感觉,调度算法应该适当的分配一个执行周期,譬如三十天执行一次(主要为了算法性能)。因为,删除虚拟机的请求外与购买服务器时产生的资源空隙虽然会一直存在,但可能并不会存留很大(大到空隙可清理出一台服务器),这可能需要一个积累的过程。当然为了尽少花钱,调度算法可以每天执行一次。
- 最后,仔细考虑调度算法,本质上是将存在别的服务器上的虚拟机转移到别的服务器上用以填补空隙,而每一个服务器的资源空隙可否假想成一台新的服务器?别的服务器上的虚拟机可否当做一个个虚拟机请求?这样一来就可以套用购买时的策略,将别的服务器上的虚拟机当做请求,去凑出当前服务器的资源空隙,这样即可实现调度。
2.2 调度算法阐述:
- 首先,假定当前已经知晓所购买的全部服务器的资源空隙,同时拥有每台服务器存储的虚拟机。
- 其次,将所购买的全部服务器按照资源空隙大小降序排序(例如:S1,S2…,Sn)。
- 接着,按顺序每次取出一台服务器S1,将S1的资源空隙当做类似购买分配算法中服务器总表的存在(Ps:当然S1的资源空隙只是一组数据,但思路类似,理解即可);并将其后所有服务器(S2…,Sn)上存储的全部的虚拟机存成一个列表,当做类似购买分配算法中的一日请求总表之类的存在,传入购买分配算法中,最终返回需要被调度填补S1资源空隙的虚拟机,并将其在原服务器中删除,重新统计S2…Sn的资源空隙。
- 然后,将S1从队列中剔除,取出S2,执行如上操作,直至队列中只剩下Sn,算法结束。
总结
Ps:
- 今天虽然伪代码没写完,但五千多字耗时4小时写完的博文(不加思考时间)已然让我力竭,见谅~
- 如果大家有什么好的思路,或者本文的不妥之处,欢迎在评论区吐槽留言~
3月14日更新
- 今日队中工作任务临时做了调整,时间有点不够,抱歉大家更晚了。分享一下输入模块,注释没写太多,明天再补。
#输入模块分享
class Host:
hostType = ''
hostCpuNum = 1
hostMemSize = 1
hostHardWareCost = 1
hostDayCost = 1
hostXingJiaBi = 1
hostCoreMemBi = 1
def xingJiaBi(self):
self.hostXingJiaBi = (self.hostHardWareCost / self.hostCpuNum) * self.hostCpuNum / (self.hostCpuNum + self.hostMemSize) + (self.hostHardWareCost / self.hostMemSize) * self.hostMemSize / (self.hostCpuNum + self.hostMemSize)
def coreMemBi(self):
self.hostCoreMemBi = self.hostCpuNum / self.hostMemSize
class Vm:
vmType = ''
vmCpuNum = 1
vmMemSize = 1
vmDoubleOrNot = 1
vmCoreMemBi = 1
def coreMemBi(self):
self.vmCoreMemBi = self.vmCpuNum / self.vmMemSize
class DayRequire:
requireNum = 0
requireType = ''
vmType = ''
vmId = ''
addRequireList = []
delRequireList = []
def readtxt(filename):
try:
f = open(filename, "r")
line = f.read()
return line
finally:
f.close()
def inputData(allData):
flag = 0
for i in range(len(allData)):
if allData[i].isdigit():
flag += 1
if flag == 1:
hostTypeNum = int(allData[i])
hostListAll = [allData[i + j + 1] for j in range(hostTypeNum)]
elif flag == 2:
vmTypeNum = int(allData[i])
vmListAll = [allData[i + j + 1] for j in range(vmTypeNum)]
elif flag == 3:
dayNum = int(allData[i])
flag1 = 0
dayListAll = [0 for j in range(dayNum)]
for j in range(i + 1, len(allData)):
if allData[j].isdigit():
flag1 += 1
if flag1 <= dayNum:
oneDay = DayRequire()
oneDay.requireNum = int(allData[j])
for k in range(j + 1, j + oneDay.requireNum):
tmpeList = allData[k].split(',')
if len(tmpeList) == 2:
oneDay.requireType = tmpeList[0].replace('(', '')
oneDay.vmId = tmpeList[1].replace(')', '').replace(' ', '')
oneDay.delRequireList.append((oneDay.requireType, oneDay.vmId))
elif len(tmpeList) == 3:
oneDay.requireType = tmpeList[0].replace('(', '')
oneDay.vmType = tmpeList[1].replace(' ', '')
oneDay.vmId = tmpeList[2].replace(')', '').replace(' ', '')
oneDay.addRequireList.append((oneDay.requireType, oneDay.vmType, oneDay.vmId))
dayListAll[flag1 - 1] = oneDay
break
#为Host类赋值,使hostListAll中每一个元素为一个Host类
i = 0
for host in hostListAll:
#分割数据去除括号
host = host.split(',')
host[0] = host[0].replace('(', '').replace(' ', '')
host[4] = host[4].replace(')', '').replace(' ', '')
#赋值数据
tmpeHost = Host()
tmpeHost.hostType = host[0]
tmpeHost.hostCpuNum = int(host[1])
tmpeHost.hostMemSize = int(host[2])
tmpeHost.hostHardWareCost = int(host[3])
tmpeHost.hostDayCost = int(host[4])
tmpeHost.xingJiaBi()
tmpeHost.coreMemBi()
hostListAll[i] = tmpeHost
i += 1
i = 0
for vm in vmListAll:
# 分割数据去除括号
vm = vm.split(',')
vm[0] = vm[0].replace('(', '').replace(' ', '')
vm[3] = vm[3].replace(')', '').replace(' ', '')
# 赋值数据
tmpeVm = Vm()
tmpeVm.vmType = vm[0]
tmpeVm.vmCpuNum = int(vm[1])
tmpeVm.vmMemSize = int(vm[2])
tmpeVm.vmDoubleOrNot = int(vm[3])
tmpeVm.coreMemBi()
vmListAll[i] = tmpeVm
i += 1
i = 0
return hostListAll, vmListAll, dayListAll
def main():
# to read standard input
# process
# to write standard output
# sys.stdout.flush()
filename = "training-1.txt"
allData = readtxt(filename).split('\n')
# print(allData[1])
[hostListAll, vmListAll, dayListAll] = inputData(allData)
if __name__ == "__main__":
main()