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

article/2025/9/15 16:18:16

文章目录

  • 2022华为软挑(初赛笔记)
    • 1. 赛题要求
    • 2. 解决方案
      • 2.1 挑选适合的边缘节点
      • 2.2 第一轮:最大分配
      • 2.3 第二轮:均值分配
    • 总结

本文仓库地址:
Github-CodeCraft-2022

2022华为软挑(初赛笔记)

1. 赛题要求

image-20220329163109918

任务核心:解决流量的调度问题,以最优分配满足客户的流量请求

  1. 共有M个客户节点和N个边缘节点。
  2. 在每个时刻,要决策如何把每个客户节点的带宽需求分配到边缘节点。
  3. 为了确保调度质量,每个客户节点的需求只能分配到满足QoS约束的边缘节点上。即:当客户节点和边缘节点之间的QoS小于“QoS上限”时,才会进行流量分配。
  4. 在每个时刻,每个边缘节点接收的带宽需求总和不能超过其带宽上限。
  5. 合理分配所有时刻的客户节点带宽需求,使得最终的带宽总成本尽量小。

计分标准:所有使用的边缘服务器95百分位流量值总和

优化目标:找到一组满足约束的流量分配方案 𝑋 使其在时间集合 𝑇 内的总带宽成本尽可能小 。

2. 解决方案

题目的核心是使得各边缘服务器95百分位流量值求和最小。首先要选择所有满足QoS上限的边缘服务器,然后核心目的使得计分位以上的点时刻该边缘节点流量跑分尽可能满,计分点以下的时刻应该尽可能均且靠近95百分位点。考虑无论是从纵向时间分配的角度,还是横向各用户需求分配的角度,都突出“削峰”思想,避免出现带宽分配集中的情况,使得分配尽量平均。

2.1 挑选适合的边缘节点

解题的第一步是要找出各个用户满足QoS限制的合适的边缘服务节点,按照各点QoS计算出用户的对应边缘节点表,并加以记录。代码如下:

# 计算有效用户节点
def find_useful_server():# 挑选出满足每个客户带宽需求的边缘节点def limit_server_qos(axis_input):index_temp = np.where(qos[:, axis_input] < qos_constraint)qos_temp = np.array((index_temp[0], qos[index_temp][:, axis_input])).Tqos_temp = qos_temp[np.argsort(qos_temp[:, 1], axis=0)]return qos_temp# 每个用户满足的节点列表# 每个用户满足的节点数量排序_client_limit_res, _client_limit_num_sequence, _client_limit_band_sequence = [], [], []for i in range(len(demand[0])):_server_res = limit_server_qos(i)_client_limit_res.append(_server_res)_client_limit_num_sequence.append(len(_server_res))_client_limit_band_sequence.append(sum([int(apply[qos_keys[x]][0]) for x in _server_res[:, 0]]))# 所有可用边缘节点集合_server_all = []# 构建所有可用边缘节点列表for x in _client_limit_res:_server_all += list(x[:, 0])_server_set = set(_server_all)# 每个客户所有满足的边缘节点的列表# 增加排序,(由于边缘节点时延目前无需考虑,直接按照序号排列),保证满足边缘节点搜索优先度_server_list = [sorted(list(_client_limit_res[i][:, 0])) for i in range(len(_client_limit_num_sequence))]# 每个客户拥有的满足边缘节点升序排序,首要排序指标数量,次要排序指标可提供带宽_server_num_sequence = np.argsort(np.array((_client_limit_num_sequence, _client_limit_band_sequence)).T, axis=0)# _server_num_sequence = [j[0] if j[0]==j[1] else j[1] for j in _server_num_sequence]_server_num_sequence = [j[0] for j in _server_num_sequence]# 排除完全没有边缘可以满足的用户while not len(_server_list[_server_num_sequence[0]]):_server_num_sequence = np.delete(_server_num_sequence, 0)# 对客户表进行排序temp_dict = {j: i for i, j in enumerate(_server_num_sequence)}_client_list = {j: [] for j in _server_set}for client, s_list in enumerate(_server_list):for s in s_list:_client_list[s].append(client)for k, cc in zip(_client_list.keys(), _client_list.values()):x = np.array([[c, temp_dict[c]] for c in cc])x = x[np.argsort(x[:, 1])][:, 0]_client_list[k] = x.tolist()# 对边缘节点按照可以被使用用户数量调整排序temp = []_client_num_sequence = []for y in _client_list.values():temp.append(len(y))temp_list = list(_server_set)for i, j in enumerate(np.argsort(temp)):_client_num_sequence.append(temp_list[j])_server_set = _client_num_sequencereturn _server_list, _server_num_sequence, _server_set, _client_list

2.2 第一轮:最大分配

刚开始使用的策略是一轮即完成分配。在一轮里按时刻遍历,先找到时刻内还有前5%值的边缘,再依次找边缘拥有的请求用户,将需求分配到该边缘,直到边缘满了或者用户需求解决。然后所有剩下的用户需求再均分。

但这种思路没有很好的解决最大需求的完美分配问题。因为各个边缘节点供给的用户最大需求来临时刻不一样,如果直接按照时间顺序会造成很多百分之五节点的浪费。经过考虑,我们决定针对总需求进行排序然后送入的方式解决这个问题。虽然最终的确可以在一定程度上解决问题,但依然存在一部分百分之五的点无法完全分配的问题。结果如图:

image-20220320143542997

最终,通过交流,我们明白了问题的原因:由于各个用户峰值来临时刻的不同,且这种不同很难通过整体流量的变化进行判断。所以应该对流量的分配进行调整,从各个边缘节点出发,让其挑选出其所属用户流量最高的1个时刻,然后进行分配。待到分配完成之后,再继续从下一个边缘节点出发,对其覆盖用户目前流量继续排序。直到五轮迭代,满足所有的边缘节点。这样的操作方法可以尽可能跑满边缘的用户流量,使其达到最大。

最终改进的代码如下:

for roll_time in range(demand_limit):for server_index in server_set:client_index_list = client_wait_pool_max[server_index]# user_demand_list 表示用户的需求数组, 第1列表示排序后的需求次序映射到原始次序的顺序, 第二列往后表示该边缘拥有的各用户按顺序的需求量user_demand_list = np.array([demand[:, index] for index in client_index_list]).Tuser_demand_list_sum = np.sum(user_demand_list, axis=1)user_sort_sequence = np.argsort(np.sum(user_demand_list, axis=0))user_demand_list = np.c_[np.arange(len(user_demand_list)), user_demand_list_sum, user_demand_list]user_demand_list = user_demand_list[np.argsort(user_demand_list[:,1], axis=0)[::-1]]for i, u_demand in enumerate(user_demand_list):if u_demand[0] not in record_time[server_index]:user_demand_list = user_demand_list[i]breakuser_demand = np.delete(user_demand_list, 1)time = user_demand[0]record_time[server_index].append(user_demand[0])# 单个时间用户的需求user_list_temp = {}server_bandwidth = int(apply[qos_keys[server_index]][0])# 遍历各个用户for index, user in enumerate(client_index_list):# query: 该用户的请求query = user_demand[index + 1]# 如果这个用户的需求超过了当前服务器闲置带宽if query >= server_bandwidth:# 分配所有闲置带宽total_res[user_demand[0]][user].append([server_index, server_bandwidth])# 如果user_list_temp有东西,说明不是一个节点填满的,写进去for u, q in zip(user_list_temp.keys(), user_list_temp.values()):# 保存分配total_res[user_demand[0]][u].append([server_index, q])# 用户请求量更新demand[user_demand[0], u] = 0# 用户请求量更新demand[user_demand[0], user] -= server_bandwidth# 边缘服务量更新server_bandwidth = 0breakelse:# 服务器待分配带宽更新server_bandwidth -= query# 用户记录队列保存user_list_temp.update({user: query})if server_bandwidth != 0:for u, q in zip(user_list_temp.keys(), user_list_temp.values()):# 保存分配total_res[user_demand[0]][u].append([server_index, q])# 用户请求量更新demand[user_demand[0], u] = 0# 使用过的时间记数max_used_time[time] += 1

2.3 第二轮:均值分配

第一轮动态分配完成之后,第二轮对于剩下的各个时刻总流量来说,已经是削峰过后的结果。考虑对剩下的待分配流量,使用可以使用的边缘结点进行动态均分。

我们尝试使用了很多办法,最终选择了基于动态分配以及最大上限的平均算法:

首先,按照总请求量从小到大进行排列,按照排序时间序列进行处理。

然后,按照可用边缘节点数量对用户进行排序,按照升序顺序逐个处理用户剩余请求。

再者,对于每个用户的请求,分为两种情况:

  1. 若该边缘节点未发生过排序,按照动态平均的思想,将动态分配期望作为所属节点的带宽期望,最终追求各个节点分配量的平均。
  2. 若该边缘节点在平均轮已经有某个时间产生过流量服务,剩下的时间以不超过前面轮次该节点产生流量的最大值作为初始期望。

最终实现的代码如下:

# 开始进行匹配
for num, demand_index in enumerate(demand_sequence):# 从时间序列上按用户需求总量降序匹配client_query_bandwidth = demand[demand_index]# 等待分配用户节点池client_wait_pool = list(server_num_sequence)# 等待分配边缘节点池server_wait_pool = server_wait_pool_max.copy()# # 单次分配结果字典# single_res = {i: [] for i in range(len(client_wait_pool))}# 全局边缘节点带宽大小server_bandwidth = {node: int(apply[qos_keys[node]][0]) for node in server_set}# 从用户角度迭代,满足用户未满足的需求# 挑选出此时刻不允许使用的边缘节点(已经被最大分配)server_useless = []for user_index, res_list in zip(total_res[demand_index].keys(), total_res[demand_index].values()):for server_list2 in res_list:server_useless.append(server_list2[0])for client in client_wait_pool:# 除了不能用的,剩下的加入列表use_server_index_list = []for s in server_list[client]:if s not in server_useless:use_server_index_list.append(s)# print(use_server_index_list)# print(use_server_index_list)query = client_query_bandwidth[client]server_temp = use_server_index_list.copy()# 迭代满足需求while query != 0:# # 计算均值策略可用各边缘节点使用率# use_rate = {node: (int(apply[qos_keys[node]][0]) - server_bandwidth[node]) / int(apply[qos_keys[node]][0])#             for node in use_server_index_list}# # 按照最大使用率差值计算各节点富余分配量# deliver_dict = {node: int((max(use_rate.values()) - use_rate[node]) * int(apply[qos_keys[node]][0]))#                 for node in use_server_index_list}deliver_dict = {}for node in use_server_index_list:if server_max_avg[node]:deliver_dict.update({node: server_max_avg[node]})else:# 计算均值策略可用各边缘节点使用率use_rate = {node: (int(apply[qos_keys[node]][0]) - server_bandwidth[node]) / int(apply[qos_keys[node]][0])for node in use_server_index_list}# 按照最大使用率差值计算各节点富余分配量deliver_dict = {node: int((max(use_rate.values()) - use_rate[node]) * int(apply[qos_keys[node]][0]))for node in use_server_index_list}# 如果请求超过了当前动态分配的结果if query >= sum(deliver_dict.values()):res_query = query - sum(deliver_dict.values())# 平均分担当前待分配带宽mean_deliver = res_query // len(use_server_index_list)left_deliver = res_query % len(use_server_index_list)for node in use_server_index_list:deliver_dict[node] += mean_deliver# 如果请求不超过当前动态分配的结果else:temp = sum(deliver_dict.values())for node in use_server_index_list:deliver_dict[node] = int(query * (deliver_dict[node] / temp))left_deliver = query - sum(deliver_dict.values())# print(deliver_dict)# print(sum(deliver_dict.values()), query-sum(deliver_dict.values())-left_deliver)tempp = []for i in use_server_index_list:tempp.append(server_bandwidth[i])tempp = np.c_[use_server_index_list, tempp]tempp = tempp[np.argsort(tempp[:,1])][:,0][::-1]for use_server_index in tempp:free = server_bandwidth[use_server_index]if left_deliver > 0:need = deliver_dict[use_server_index] + left_deliverleft_deliver = 0else:need = deliver_dict[use_server_index]# # 如果待分配带宽为0# if free == 0:#     continue# 如果分配请求超过该节点可提供带宽上限if need > free:# 存储单个分配节点信息total_res[demand_index][client].append([use_server_index, free])# 请求总量减小query -= free# 均值策略该节点待分配量归0server_bandwidth[use_server_index] = 0use_server_index_list.remove(use_server_index)else:# 分配量为均值total_res[demand_index][client].append([use_server_index, need])# 请求总量减小query -= need# 均值策略该节点待分配量减小server_bandwidth[use_server_index] -= need# print(server_bandwidth)for index, value in zip(server_bandwidth.keys(), server_bandwidth.values()):if index in server_temp:server_max_avg[index] = max(server_max_avg[index], int(apply[qos_keys[index]][0]) - value)

总结

我们参赛历时大概十天,对处理的方法也经过不断的讨论调整。虽然最终的代码依然还有很大的改进空间,例如最大轮的分配不以简单吃完用户需求为策略,而是追求去均匀吃下用户的需求;最大轮里边缘的使用时间不要过于集中,避免“过削峰”;平均策略里应该尝试进行多次分配寻找最佳均值等。

但是,作为第一次参加的算法比赛,还是有很大的收获的。认识到理论的想法到实际的工程化实现之间依然有很远的距离,想要实现idea不是那么容易的。吸取教训,希望下次能够有更好的成绩!


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

相关文章

2023华为软件精英挑战赛笔记心得(Python实现)

第一次参加华为软挑&#xff0c;问了周围一圈人没人组队&#xff0c;看了眼题目&#xff0c;感觉挺有意思的&#xff0c;就打算自己写来跑一下&#xff0c;不求分数&#xff0c;主要是想学点东西&#xff0c;顺便记录一下。&#xff08;最后跑了195w&#xff0c;自己的能力也就…

2021华为软件精英挑战总结

2021华为软挑32强总结 今年的软挑最终止步于粤港澳赛区第16名&#xff0c;总成本为16亿3979万6349&#xff0c;赛区第一名总成本为15亿3903万4817。 虽然没进入决赛&#xff0c;但是拿到了华为面试直通卡&#xff0c;也喜提广州一日游&#xff0c;算不虚此行了。决赛虽然还在继…

Spring认证中国教育管理中心-Spring Data Neo4j教程一

原标题&#xff1a;Spring认证中国教育管理中心-Spring Data Neo4j教程一&#xff08;Spring中国教育管理中心&#xff09; 5. 开始 我们为 SDN 提供了 Spring Boot 启动器。请通过您的依赖管理包含启动模块并配置要使用的螺栓 URL&#xff0c;例如org.neo4j.driver.uribolt:/…

SpringBoot 整合 Neo4j

1、创建测试类2、集成 SpringBoot 阅读此文之前&#xff0c;必须对 Neo4j 有个初步的了解&#xff0c;如果要实际操作的话&#xff0c;需要自备一个 Neo4j 数据库 本文所涉及代码已开源至 Gitee https://gitee.com/Array_Xiang/spring-boot-neo4j 创建一个 SpringBoot 项目&…

【Neo4j教程之CQL函数基本使用】

&#x1f680; Neo4j &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;C…

Neo4j资料 Neo4j教程 Neo4j视频教程 Neo4j 图数据库视频教程

课程发布地址 地址&#xff1a; 腾讯课堂《Neo4j 图数据库视频教程》 https://ke.qq.com/course/327374?tuin442d3e14 作者 庞国明&#xff0c;《Neo4j权威指南》副主编、《Neo4j 3.x 入门经典》翻译 邮箱&#xff1a;pangguomingyeah.netQQ:1143815700Neo4j技术讨论QQ群&…

Neo4J超详细专题教程,快来收藏起来吧

Neo4J超详细教程 Lecture&#xff1a;波哥 一、Neo4J相关介绍 1.为什么需要图数据库 随着社交、电商、金融、零售、物联网等行业的快速发展&#xff0c;现实社会织起了了一张庞大而复杂的关系 网&#xff0c;传统数据库很难处理关系运算。大数据行业需要处理的数据之间的关系随…

Neo4j教程 Neo4j视频教程 Neo4j 图数据库视频教程

课程发布地址 地址&#xff1a; 腾讯课堂《Neo4j 图数据库视频教程》 https://ke.qq.com/course/327374?tuin442d3e14 作者 庞国明&#xff0c;《Neo4j权威指南》副主编、《Neo4j 3.x 入门经典》翻译 邮箱&#xff1a;pangguomingyeah.netQQ:1143815700Neo4j技术讨论QQ群&…

neo4j教程-安装部署

neo4j教程-安装部署 Neo4j的关键概念和特点 •Neo4j是一个开源的NoSQL图形存储数据库&#xff0c;可为应用程序提供支持ACID的后端。Neo4j的开发始于2003年&#xff0c;自2007年转变为开源图形数据库模型。程序员使用的是路由器和关系的灵活网络结构&#xff0c;而不是静态表…

Neo4j安装教程

1.下载社区版本&#xff0c;java8推荐安装3.*的版本 Neo4j Download Center - Neo4j Graph Data Platformhttps://neo4j.com/download-center/#community 点击下载即可。 2.配置 启动 将提取的文件放在服务器上的永久主页中&#xff0c;例如 D:\neo4j\. 顶级目录称为 NEO4J_…

Neo4j详细介绍及使用教程

文章目录 一、Neo4j介绍1.Neo4j简介2.图数据库简介3.Neo4j的优缺点4.Neo4j的常见应用场景二、使用教程1.下载安装2.数据插入和查询(1)基本概念(2)基本语法Ⅰ.CREATE操作——创建Ⅱ.MERGE——创建或更新Ⅲ.Match操作——查找指定的图数据Ⅳ.DELETE操作——删除节点3.JAVA实战 一…

Neo4j语法教程

neo4j简版教程 create (<node-name:<label-name2>:<label-name2>......>) return <node-name> 可以给一个节点创建多label的node eg: CREATE (dept:Dept { deptno:10,dname:"Accounting",location:"Hyderabad" }) Neo4j CQL创…

【数据库】linux安装neo4j教程(neo4j 4.x)

一.配置jdk neo4j 4.x版本依赖jdk11&#xff0c;需要安装jdk11才能正常启动&#xff08;安装高版本或低版本jdk都不行&#xff09; 1&#xff09;执行uname -a看下系统架构 2&#xff09;根据系统架构下载对应安装包 https://www.oracle.com/java/technologies/javase/jdk11…

linux neo4j 教程,Neo4j 入门教程 - 安装

本篇来简单介绍下如何下载并安装 Neo4j&#xff0c;篇目很短&#xff0c;因为真的很简单。 下载 Neo4j 首先在 https://neo4j.com/download/ 下载 Neo4j。你可以选择企业体验版或者免费的社区版&#xff0c;这里我是用的社区版。点击 Download 按钮即可开始下载。 网站会自动下…

使用Docker安装neo4j教程

拉取镜像源 docker pull neo4j(:版本号) //缺省 “:版本号” 时默认安装latest版本的查看本地镜像 docker images启动容器 docker run -d --name container_name -p 7474:7474 -p 7687:7687 -v /home/neo4j/data:/data -v /home/neo4j/logs:/logs -v /home/neo4j/conf:/var…

neo4j教程 java_neo4j 教程

Neo4j是一个世界领先的开源图形数据库。 它是由Neo技术使用Java语言完全开发的。本教程将教你Neo4j的基础知识&#xff0c;Java与Neo4j和Spring DATA与Neo4j。 本教程分为Neo4j简介&#xff0c;Neo4j CQL&#xff0c;Neo4j CQL函数&#xff0c;Neo4j管理员&#xff0c;Neo4j与J…

最详细的Neo4J解读(附安装教程)

文章目录 一、Neo4j简介二、Neo4j - 特点和优势1.Neo4j的特点2.Neo4j的优点3.Neo4j的缺点或限制 三、Neo4j - 数据模型四、Neo4j安装及配置1.安装Java JDK2.下载安装Neo4j3.创建系统环境变量4.Neo4j的启动和停止5.切换数据库 五、Neo4j的CQL操作 一、Neo4j简介 Neo4j是一种流行…

图数据库Neo4j实战(全网最详细教程)

1.图数据库Neo4j介绍 1.1 什么是图数据库&#xff08;graph database&#xff09; 随着社交、电商、金融、零售、物联网等行业的快速发展&#xff0c;现实社会织起了了一张庞大而复杂的关系网&#xff0c;传统数据库很难处理关系运算。大数据行业需要处理的数据之间的关系随数…

neo4j入门

目录 一、安装 二、CQL使用 三、Springboot(2.4以上版本)整合neo4j 四、使用过程中的问题 1、自定义查询&#xff0c;cql无法接收变量 2、使用依赖去操作neo4j只有return才会执行 3、neo4j和mysql事务冲突 补充 一、安装 1、首先要配置jdk&#xff0c;默认电脑中有jdk…

Neo4j 安装、使用教程

文章目录 一、Neo4j 的安装与配置1、安装JDK2、安装Neo4j3、Neo4j环境变量配置4、启动服务器 二、Neo4j 使用教程 一、Neo4j 的安装与配置 1、安装JDK 由于Neo4j是基于Java的图形数据库&#xff0c;运行Neo4j需要启动JVM进程&#xff0c;因此必须安装JAVA SE的JDK。配置 JDK环…