2021华为软挑赛题_思路分析——实时更新,做多少更多少(五)
前言
- 时间陆陆续续,这已经是更新的第8天,这个专栏的第5篇文章了。多谢诸位朋友的点赞、支持、评论,支撑着笔者不断向前,期望不辜负每一份朋友的喜爱~
- 随着文章越更越多,本专栏的内容到底该如何食用,笔者在此做出如下建议,望能为参赛的诸位节省宝贵的时间和精力:
- 最后,明日可能会因为鄙人的私事,暂缓更新,望诸位见谅,之后有空尽力补更~
3月17日更新
1.今日更新——双节点请求购买服务器
- 代码结构大的调整主要在:
- 将昨日数据预处理部分单独提出为dataPraparation()方法;
- 新增 doubleShopping()方法,用于处理双节点请求,购买双节点服务器;
- 新增OutPut类,便于处理最后的输出,目前暂时没用到太多。
- 剩下一些小的调整,可能罗列不全,见谅:
- Host类新增了构造方法,主要是新增一个addVmList 列表,用于为服务器记录加入其中的虚拟机,便于之后调度,删除等工作。
- 本次只更新双节点请求所需购买服务器的问题,但单节点的实则与双节点类似,有兴趣的朋友可以试着参考双节点的动手试试,期待能见到评论区的不吝分享。
# 代码解决2021华为软件挑战赛中的双节点请求购买服务器
# 所有注释为了便于说明
# 以training-1.txt中的数据为例
# 代码中注释掉的print,多数可自行解封看看预期结果
# !!!*****!!! 为昨日代码的出错点
# 技艺不精,私事繁忙,照顾不周,多担待!
# 导入numpy库
import numpy
# 服务器类
class Host:
hostType = '' # 服务器类型
hostCpuNum = 1 # 服务器CPU数
hostMemSize = 1 # 服务器内存容量
hostHardWareCost = 1 # 服务器硬件开销
hostDayCost = 1 # 服务器每日开销
hostXingJiaBi = 1 # 服务器性价比:(硬件开销*(核数/核数+内存容量)/核数) + (硬件开销*(内存容量/核数+内存容量)/内存容量)
hostCoreMemBi = 1 # 服务器核内比:核数/内存容量
cpuNumDValue = 1 # 凑数后cpuNum的差值
memSizeDValue = 1 # 凑数后memSize的差值
# 临时存储由凑数过程产生的每一台服务器中所添加的虚拟机相关信息的列表
def __init__(self):
self.addVmList = [] # 存储虚拟机的相关信息
# 列表中依次为requireType, vmType, vmId,
# # vmCpuNum, vmMemSize, vmDoubleOrNot, vmCoreMemBi
# 计算性价比
# !!!认真观察的道友应该能发现,此处公式与昨日略有不同,但与昨日数值结果相同,可自己细品,理解鄙人定义的性价比的意义!!!
def xingJiaBi(self):
self.hostXingJiaBi = ((self.hostHardWareCost * self.hostCpuNum / (self.hostCpuNum + self.hostMemSize)) / self.hostCpuNum) + ((self.hostHardWareCost * self.hostMemSize / (self.hostCpuNum + self.hostMemSize)) / 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 = ''
averageDoubleCoreMemBi = ''
averageSingleCoreMemBi = ''
# !!!昨日出错重点,以前一直没发现,pyhton类中定义的列表是全局变量!!!
# 解决方法:通过初始化方法,将列表定义为self的局部变量
def __init__(self):
self.addRequireList = []
self.addRequireList_Double = [] # 列表中为一日请求中的双节点请求
# 且按照averageDoubleCoreMemBi是否大于1
# 按照cpuNum升序(或者memSize升序排序)
# 列表中依次为requireType, vmType, vmId,
# vmCpuNum, vmMemSize, vmDoubleOrNot, vmCoreMemBi
self.addRequireList_Single = []
self.delRequireList = []
# !!!增加了两个为列表赋值的函数,只为美化代码!!!
def addRequireListAppend(self):
self.addRequireList.append([self.requireType, self.vmType, self.vmId])
def delRequireListAppend(self):
self.delRequireList.append([self.requireType, self.vmId])
# 计算每日add请求平均核内比
def averageCoreMemBi(self):
tempList = [require[6] for require in self.addRequireList_Double]
self.averageDoubleCoreMemBi = numpy.mean(tempList)
tempList = [require[6] for require in self.addRequireList_Single]
self.averageSingleCoreMemBi = numpy.mean(tempList)
# 输出类
class OutPut:
buyHostNumAll = 1 # 购买服务器总数量
buyHostType = '' # 购买服务器类型
buyHostNumOne = 1 # 购买单款服务器的数量
migrationNumAll = 1 # 总的迁移次数
migrationVmId = '' # 迁移虚拟机Id
migrationHostId = '' # 迁移目的地的服务器Id
migrationHostPoint = '' # 迁移目的地的服务器节点
vmHostId = '' # 虚拟机分配到的服务器的Id
vmHostPoint = 'A' # 虚拟机分配到的服务器的节点
def __init__(self):
self.oneDayBuyHostTempList = [] # 暂存购买服务器的相关信息
self.oneDayBuyHostList = [] # 一日购买服务器列表
# 每一项为(buyHostType, buyHostNumOne)
self.oneDayMigrationList = [] # 一日迁移列表
# 每一项为(migrationVmId, migrationHostId, migrationHostPoint)
# 或者(migrationVmId, migrationHostId)
self.oneDayDistributionList = [] # 一日分配列表
# 每一项为(vmHostId, vmHostPoint)
# 或者(vmHostId)
# 一次性读入全部文件
def readTxt(filename):
try:
f = open(filename, "r")
line = f.read()
return line
finally:
f.close()
#按核内比分类
def classifyByCoreMemBi(hostListAll):
# 核内比大于一的分一类,小于一的分一类
hostCoreMemBiList = [host.hostCoreMemBi for host in hostListAll]
hostCoreMemBiList = numpy.array(hostCoreMemBiList)
posCoreMemBiBig = numpy.argwhere(hostCoreMemBiList > 1)
# print(posCoreMemBiBig)
hostCoreMemBiBigList = [hostListAll[n[0]] for n in posCoreMemBiBig]
posCoreMemBiSmall = numpy.argwhere(hostCoreMemBiList <= 1)
hostCoreMemBiSmallList = [hostListAll[n[0]] for n in posCoreMemBiSmall]
return hostCoreMemBiBigList, hostCoreMemBiSmallList
def sortByAverageCoreMemBi(dayList):
# 处理Double
if dayList.averageDoubleCoreMemBi >= 1:
# 取出vmCpuNum存成列表
vmCpuNumTempList = [addRequireList_Double[3] for addRequireList_Double in dayList.addRequireList_Double]
vmCpuNumTempList = numpy.array(vmCpuNumTempList)
# 按照vmCpuNum升序排序
posVmCpuNumList = numpy.argsort(vmCpuNumTempList)
dayList.addRequireList_Double = [dayList.addRequireList_Double[n] for n in posVmCpuNumList]
else:
# 取出vmMemSize存成列表
vmMemSizeTempList = [addRequireList_Double[4] for addRequireList_Double in dayList.addRequireList_Double]
vmMemSizeTempList = numpy.array(vmMemSizeTempList)
# 按照vmMemSize升序排序
posVmMemSizeList = numpy.argsort(vmMemSizeTempList)
dayList.addRequireList_Double = [dayList.addRequireList_Double[n] for n in posVmMemSizeList]
# 处理Single
if dayList.averageSingleCoreMemBi >= 1:
# 取出vmCpuNum存成列表
vmCpuNumTempList = [addRequireList_Single[3] for addRequireList_Single in dayList.addRequireList_Single]
vmCpuNumTempList = numpy.array(vmCpuNumTempList)
# 按照vmCpuNum升序排序
posVmCpuNumList = numpy.argsort(vmCpuNumTempList)
dayList.addRequireList_Single = [dayList.addRequireList_Single[n] for n in posVmCpuNumList]
else:
# 取出vmMemSize存成列表
vmMemSizeTempList = [addRequireList_Single[4] for addRequireList_Single in dayList.addRequireList_Single]
vmMemSizeTempList = numpy.array(vmMemSizeTempList)
# 按照vmMemSize升序排序
posVmMemSizeList = numpy.argsort(vmMemSizeTempList)
dayList.addRequireList_Single = [dayList.addRequireList_Single[n] for n in posVmMemSizeList]
# print(dayList.addRequireList_Double)
# 数据预处理
def dataPraparation(hostListAll, vmListAll, dayListAll):
# 将服务器列表按照性价比排序
# 取出hostXingJiaBi单做一个列表
hostXingJiaBiList = [host.hostXingJiaBi for host in hostListAll]
# print(hostXingJiaBiList)
# 调用numpy.argsort函数排序返回下标
hostXingJiaBiList = numpy.array(hostXingJiaBiList)
# print(hostXingJiaBiList)
posHostXingJiaBiList = numpy.argsort(hostXingJiaBiList)
# print(pos)
hostListAll = [hostListAll[n] for n in posHostXingJiaBiList]
# 服务器按照核内比分类
[hostCoreMemBiBigList, hostCoreMemBiSmallList] = classifyByCoreMemBi(hostListAll)
# 匹配每日请求中虚拟机型号与虚拟机总表中的虚拟机型号
# 增添addRequireList的数据
vmTypeListAll = [vm.vmType for vm in vmListAll]
vmTypeListAll = numpy.array(vmTypeListAll)
# 获取全部add请求的列表
addRequireListAll = [day.addRequireList for day in dayListAll]
# print(addRequireListAll)
# 为dayListAll中的addRequireList增添vmCpuNum,vmMemSize,vmDoubleOrNot,vmCoreMemBi
for i in range(len(dayListAll)):
# 取出一日的请求
vmTypeOneDayList = [oneRequire[1] for oneRequire in addRequireListAll[i]]
# print(vmTypeOneDayList)
posVmTypeListAll = [numpy.argwhere(vmTypeListAll == vmTypeOne) for vmTypeOne in vmTypeOneDayList]
# print(posVmTypeListAll[0][0][0])
# 为每一天的一项add请求增添vmCpuNum,vmMemSize,vmDoubleOrNot,vmCoreMemBi元素
# !!!为了保证符合标准输出,此处加上每条add请求的原始位置!!!
for j in range(len(posVmTypeListAll)):
# print(posVmTypeListAll[j][0][0])
temp = [vmListAll[posVmTypeListAll[j][0][0]].vmCpuNum,
vmListAll[posVmTypeListAll[j][0][0]].vmMemSize,
vmListAll[posVmTypeListAll[j][0][0]].vmDoubleOrNot,
vmListAll[posVmTypeListAll[j][0][0]].vmCoreMemBi,
j]
# print(temp)
# print(dayListAll[i].addRequireList[0])
dayListAll[i].addRequireList[j].extend(temp)
# print(dayListAll[0].addRequireList[j])
# 将addRequireList按单双节点分类
if dayListAll[i].addRequireList[j][5] == 1:
dayListAll[i].addRequireList_Double.append(dayListAll[i].addRequireList[j])
else:
dayListAll[i].addRequireList_Single.append(dayListAll[i].addRequireList[j])
dayListAll[i].averageCoreMemBi()
# 判断单、双节点请求的平均核内比是否大于1
# 若大于1,将两个列表按照vmCpuNum(核数)将vm升序排列
# 否则,按照vmMemSize(内存容量)升序排列
sortByAverageCoreMemBi(dayListAll[i])
# print(hostCoreMemBiBigList)
return hostCoreMemBiBigList, hostCoreMemBiSmallList
def doubleShopping(oneDayOutput, dayListOne, hostCoreMemBiBigSmallList):
# white判断请求列表是否为空
while len(dayListOne.addRequireList_Double) != 0:
# 遍历hostCoreMemBiBigList服务器中双节点列表
cpuNumDValueTempList = [0 for k in range(len(hostCoreMemBiBigSmallList))]
memSizeDValueTempList = [0 for k in range(len(hostCoreMemBiBigSmallList))]
for k, hostCoreMemBiBigSmall in enumerate(hostCoreMemBiBigSmallList):
# 用于累加
sumVmCpuNum = 0
sumVmMemSize = 0
# 取出vmCpuNum,便于累加
vmCpuNumTempList = [oneRequire[3] for oneRequire in dayListOne.addRequireList_Double]
vmMemSizeTempList = [oneRequire[4] for oneRequire in dayListOne.addRequireList_Double]
for j in range(len(vmCpuNumTempList)):
if hostCoreMemBiBigSmall.hostCpuNum >= sumVmCpuNum and hostCoreMemBiBigSmall.hostMemSize >= sumVmMemSize:
sumVmCpuNum += vmCpuNumTempList[j]
sumVmMemSize += vmMemSizeTempList[j]
hostCoreMemBiBigSmall.addVmList.append(dayListOne.addRequireList_Double[j])
else:
pos = j - 1
sumVmCpuNum -= vmCpuNumTempList[pos]
sumVmMemSize -= vmMemSizeTempList[pos]
hostCoreMemBiBigSmall.cpuNumDValue = hostCoreMemBiBigSmall.hostCpuNum - sumVmCpuNum
hostCoreMemBiBigSmall.memSizeDValue = hostCoreMemBiBigSmall.hostMemSize - sumVmMemSize
break
# print(hostCoreMemBiBig.memSizeDValue)
# 取出差值存入列表
cpuNumDValueTempList[k] = hostCoreMemBiBigSmall.cpuNumDValue
memSizeDValueTempList[k] = hostCoreMemBiBigSmall.memSizeDValue
# 找到cpuNum差值最小的服务器,此时只考虑cpuNum
if dayListOne.averageDoubleCoreMemBi >= 1:
oneDayOutput.oneDayBuyHostTempList.append(hostCoreMemBiBigSmallList[cpuNumDValueTempList.index(min(cpuNumDValueTempList))])
else:
oneDayOutput.oneDayBuyHostTempList.append(hostCoreMemBiBigSmallList[memSizeDValueTempList.index(min(memSizeDValueTempList))])
# 将原列表中已经添加的相关add请求,去除已经添加过的部分
dayListOne.addRequireList_Double = dayListOne.addRequireList_Double[(pos + 1):]
# 购买、分配算法
def shoppingAndDistribution(hostCoreMemBiBigList, hostCoreMemBiSmallList, dayListAll):
# 若平均核内比 > 1,
# 分别遍历dayList.addRequireList_Double与dayList.addRequireList_Single列表中的
# 每个虚拟机请求对应的的vmCpuNum(核数),并进行累加与服务器的cpuNum比较
# 否则遍历MemSize
# 尝试凑数、分配
# 遍历dayListAll
for i in range(len(dayListAll)):
# 创建Output类
oneDayOutput = OutPut()
# 判断双节点平均核内比
# 大于1,表明cpuNum大,在hostCoreMemBiBigList中寻找购买的服务器
# 小于1,表明memSize大,在hostCoreMemBiSmallList中寻找购买的服务器
if dayListAll[i].averageDoubleCoreMemBi >= 1:
doubleShopping(oneDayOutput, dayListAll[i], hostCoreMemBiBigList)
else:
doubleShopping(oneDayOutput, dayListAll[i], hostCoreMemBiSmallList)
# 打印出来的是每一天双节点虚拟机请求所需要购买的服务器数量
print(len(oneDayOutput.oneDayBuyHostTempList))
# 判断单节点平均核内比
# 与上方处理步骤类似
# 不过需要将服务器劈成两半考虑
# 所以略有不同
# 朋友可以自觉填补
# 今日更新代码先到这里
# 明日因为鄙人私事
# 决定暂缓更新代码
# 见谅~
# 将输入的数据处理,输出为hostListAll, vmListAll, dayListAll列表
# hostListAll, vmListAll 分别代表服务器总表与虚拟机总表
# dayListAll中存储800天的数据,每一个列表元素为一个类(oneDay)
def inputData(allData):
flag = 0
for i in range(len(allData)):
if allData[i].isdigit():
flag += 1
# flag=1 准备处理出服务器总表
if flag == 1:
hostTypeNum = int(allData[i])
hostListAll = [allData[i + j + 1] for j in range(hostTypeNum)]
# flag=2 准备处理出虚拟机总表
elif flag == 2:
vmTypeNum = int(allData[i])
vmListAll = [allData[i + j + 1] for j in range(vmTypeNum)]
# flag=3 接收所有天的全部请求 本页代码中的小弯弯
elif flag == 3:
dayNum = int(allData[i])
flag1 = 0
dayListAll = [0 for j in range(dayNum)] #存DayRequire类
# 遍历800日后的全部输入 可体会一下同级下的break
for j in range(i + 1, len(allData)):
if allData[j].isdigit():
flag1 += 1
# 读不懂可解开下句封印 尝试观察输出
# print(allData[j], allData[j+1])
#过滤请求类数据,接收每日请求的数值 譬如第一日142
if flag1 <= dayNum:
#新建一个类 用于接收每一天的请求数据
oneDay = DayRequire()
oneDay.requireNum = int(allData[j])
# 读不懂可解开下句封印 尝试观察输出
# print(oneDay.requireNum)
# 下方for循环接收每一天的请求数据 存到oneDay的类中 传给dayListAll
for k in range(j + 1, j + oneDay.requireNum + 1):
# 按逗号切分每条请求
tmpeList = allData[k].split(',')
# 切分后的数组若只有两项表明为del请求 传给oneDay.delRequireList
if len(tmpeList) == 2:
# 清除多余空格、括号
oneDay.requireType = tmpeList[0].replace('(', '')
oneDay.vmId = tmpeList[1].replace(')', '').replace(' ', '')
oneDay.delRequireListAppend()
# 切分后的数组若有三项表明为add请求 传给oneDay.addRequireList
elif len(tmpeList) == 3:
# 清除多余空格、括号
oneDay.requireType = tmpeList[0].replace('(', '')
oneDay.vmType = tmpeList[1].replace(' ', '')
oneDay.vmId = tmpeList[2].replace(')', '').replace(' ', '')
oneDay.addRequireListAppend()
dayListAll[flag1 - 1] = oneDay
# 执行完flag==3中的内容后,可直接跳出最外层for循环
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
# 为Vm类赋值,使vmListAll中每一个元素为一个Vm类
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()
#以training-1.txt为示例
filename = "training-1.txt"
#按换行分割全部数据
allData = readTxt(filename).split('\n')
# 根据下一条注释可以返回分割后的数据
# print(allData[0])
[hostListAll, vmListAll, dayListAll] = inputData(allData)
# 数据预处理
[hostCoreMemBiBigList, hostCoreMemBiSmallList] = dataPraparation(hostListAll, vmListAll, dayListAll)
# 购买分配算法
shoppingAndDistribution(hostCoreMemBiBigList, hostCoreMemBiSmallList, dayListAll)
# 根据下一条的输出,可显示第一天第一条请求
# print(dayListAll[0].addRequireList[0])
if __name__ == "__main__":
main()
总结
Ps:
- 最后,随着变量的越写越多,昨日自称起名鬼才的鄙人,看着变量名越来越长也渐感无力,望朋友多担待,有机会试着整理一下全部变量名~
- 如果大家有什么好的思路,或者本文代码的不妥之处,欢迎在评论区吐槽留言~