项目二:2020年华为软件精英挑战赛

2020年华为软件精英挑战赛总结

成绩:

  • 初赛成渝赛区排名第9 (共1034支队伍)
  • 复赛成渝赛区排名第3 (共32支队伍),进入决赛
    项目背景:通过金融风控的资金流水分析,可有效识别循环转账,辅助公安挖掘洗钱组织,帮助银行预防信用卡诈骗。基于给定的资金流水,检测并输出指定约束条件的所有循环转账。要求结果准确,用时最短。

2020年华为软件精英挑战赛面试

主要学习弄懂三个知识点 :dijkstra(基本算法流程与优化方法),计算bc算法(基本算法流程),代码优化技巧(记几个,加上原理)

向面试官介绍自己的比赛,四个方面来说:
比赛背景: 基于给定的资金流水,检测并输出指定约束条件的所有循环转账,可有效识别循环转账,帮助银行预防信用卡诈骗。要求结果准确,用时最短。分为初赛,复赛,决赛三个阶段,题目各不相同;
比赛负责内容: 主要负责三方面工作,第一负责解题算法研究,包括Dijkstra算法研究,位置关键中心性算法研究;第二参与代码优化,包括使用一些c++代码优化加速技巧;第三参与数据测试与分析工作,包括分析数据特点与服务器测试代码得分;
比赛成果: 初赛成渝赛区排名第9 (共1034支队伍);复赛成渝赛区排名第3 (共32支队伍);决赛全国总排名第9;

初赛

流程:读图预处理(去除特殊节点)->储存图(前向星)->dfs找环(正向3+反向4,bfs与dfs区别)->输出

优化:
1.使用局部变量,减少使用全局变量;
2.使用数组代替vector;
3.保持结构体字节对齐
4.将id和边尽可能的连续,提高cache的命中率
5.更改数据类型,减少计算时间
针对数据特点优化:
转账金额较小的这一点,可以使用动态的数据类型,如自动切换的uint_64,uint_32,uint_16(提升100+)
第一组:菊花图(无标度网络),存在大量入度为0,出度为1的结点
第二组:随机图,90%以上的点只有一个前驱,边权为100以内的正态分布,均匀度为20左右,且分布均匀,因此搜索四次邻接表即可完成全图搜索对任意一点,所有点均会入堆至少一次
第三组:第一组+第二组综合

决赛

流程:读图预处理->储存图->dijkstra计算最短路+优先队列->计算位置关键中心性->输出
基本思想:以每个点出发做dijstra,求出到个点的单源最短路径,并且按拓扑序反向递推计算中心性bc

优化:
1.使用局部变量,减少使用全局变量;
2.使用数组代替vector;
3.保持结构体的定义 保持字节对齐
4.将id和边尽可能的连续,提高cache的命中率
5.更改数据类型,减少计算时间

一、初赛

前向星资料1,前向星资料2

(1)题目

输入:给定一个有向图,求出图中所有长度在[3,7]之间的环。
格式为[IDU,IDV,Weight]的边表,ID为32位无符号整数(当然题目说明了,小于2^31,所以int32就好),边最多28W条,不重复,结点平均度数小于10。环中同一个ID不可以重复出现(若大环包括小环,则需要分开统计),环的个数不大于300W。

输出:环的个数,按照1.环长度;2.环的数字字典序输出所有环。

步骤:

  1. 将输入资金流读取,获取id,排序并去除重复的;
  2. 使用DFS深度优先遍历算法找出所有环

总结::有向图找环,确立解题步骤:读文件->建图->dfs找环->结果输出到文件。
项目二:2020年华为软件精英挑战赛_第1张图片

(2)优化小技巧

分为三个方面:
数据结构,多线程优化,其他优化

  • 改变图的存储结构,使用二维数组储存邻接表结构的图,提高访问速度(尽量减少STL容器的使用)
  • 加入剪枝,如果一个点的入度与出度为0,直接跳过
  • 不要预先给各个线程分配任务,这样可能导致任务分配不均匀。使用动态分配策略:维护一个队列,如果一个线程完成了一个任务,那么就去队列里去取下一个任务。
  • 排除深度在小于3或者大于7的、每次访问的结点ID都应该大于起点的ID
    项目二:2020年华为软件精英挑战赛_第2张图片

(3)知识点

深度优先遍历
深度优先搜索算法(Depth First Search,简称DFS):一种用于遍历或搜索树或图的算法。 沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过或者在搜寻时结点不满足条件,搜索将回溯到发现节点v的那条边的起始节点。整个进程反复进行直到所有节点都被访问为止。

  • 深度优先遍历与广度优先遍历的区别与联系
  • 深度优先遍历与广度优先遍历的理解

深度优先遍历的时候维护一个当前栈,如果遇到点在当前栈内,那么就碰到了一个环,从栈里面取出这个环即可。

二、复赛

要求:
转账记录 28W -> 200W
环个数 2914186 -> 2000W
转账金额比例约束 :如[A,B,X],[B,C,Y],要满足0.2 <= Y/X <= 3
ID为32位的无符号整数,转账金额为32位的正整数,且都小于2的31次方,选手可以不用考虑范围之外的输入。(转账金额不为0的意思,但所有数据依然为int32范围)

相较于初赛,转账记录从300w条扩充至2000w,同时限制转账前后路径的金额浮动为:0.2-3;输入金额从uint32变为浮点数(小数点后最多两位),环路从3-7改为3-8

三、决赛

(1)题目

输入:
输入为包含资金流水的文本文件,每一行代表一次资金交易记录,包含本端账号ID, 对端账号ID, 转账金额,用逗号隔开;四点要求:
 本端账号ID和对端账号ID为一个32位的无符号整数
 转账金额为一个32位的无符号整数
 转账记录最多为250万条
 账号A给账号B最多转账一次

输出:
输出信息为一个文件,包含TOP 100的账户信息:
从第一行开始,每一行的信息为账户ID和位置关键中心性(四舍五入,有效位数保留小数点后3位),用逗号分开。总体按照位置关键中心性的降序输出(先比较位置关键中心性再精确到小数点后3位);如果位置关键中心性相同,则按照账户ID字典序(ID转为无符号整数后)升序输出。请注意:位置关键中心性为0,且能排到TOP100时,也需要输出。

总结题意就是:计算每个id的位置关键中心性并输出top100/计算有向图中每个点的中介中心性,取top100输出

(2)优化技巧

  • dijkstra+数组–>dijkstra+优先队列–>dijkstra+斐波那契堆
  • dijkstra+堆 优先队列里面只存储不重复的dist,重复的dist被放到一个vector中
  • 面向数据优化+int/short,更改数据类型,减少计算时间
  • 将id和边尽可能的连续,提高cache的命中率,参考资料
  • 共享变量结构定义为Cache line对齐
  • 减少使用全局变量,增强数据访问的局部性
  • 指令级并行(去除指令间的依赖关系),
  • 向量化井行,循环并行化(串行循环转化为并行循环)
  • 循环展开,流水线
  • 计算量大的时候,进行分片,将计算量限定在cache容量以内,降低cache miss以提升计算性能
  • 尽量保持结构体的定义 保持字节对齐,有利于在编译器优化后运行时性能最佳,避免内存不对齐。

(3)知识点

每一个点dijkstra算法,求最短路,单源最短路径

DFS与BFS(深度优先遍历与广度优先遍历)

介数中心性

介数中心性,参考资料1,参考资料2
参考资料3
介数中心性betweenness反映了节点v作为“桥梁”的重要程度。
资料4

计算图中节点的介数中心性分为两种情况: 有权图上的介数中心性和无权图上的介数中心性。两者的区别在于求最短路径时使用的方法不同,对于无权图采用BFS(宽度优先遍历)求最短路径,对于有权图采用Dijkstra算法求最短路径。
简单介绍下BFS为什么可以做。我们知道BFS遍历节点的顺序恰好是按照距离节点ss的距离升序排列的,而节点uu是vv的前驱节点必然要求d(s,u)

最短路

//Dijkstra 伪代码
Dijkstra(G,d[],s)
{
    初始化;
    for(循环n次)
    {
        u=使d[u]最小的还未被访问过的顶点的标号;
        记u已被访问;
        for(从u出发能到达的所有顶点v)
        {
            if(v未被访问&&以u为中介点使s到顶点v的最短距离最优)
            {
                优化d[v];
            }
        }
    }
}
  • 最短路算法,Dijkstra算法(及其改进:BF算法,SPFA算法),Floyd算法
  • Dijkstra算法优化1
  • Dijkstra算法优化2
  • Dijkstra算法及其堆优化3
  • Dijkstra算法及其堆优化4

堆优化的主要思想就是使用一个优先队列(就是每次弹出的元素一定是整个队列中最小的元素)来代替最近距离的查找,用邻接表代替邻接矩阵,这样可以大幅度节约时间开销。

Dijkstra算法本质上是一种贪心算法,1959年,Edsger Dijkstra提出了该算法用于解决单源最短路径问题,即在给定每条边的长度l,求取源节点s到其它所有节点的最短路径。Dijkstra算法维护一个节点集合S,S中的节点到源点的最短距离d已经确定了,即“Explored”。普通的Dijkstra算法复杂度为n平方,当数据量稍大时就会超时,所以诞生了时间复杂度为nlogn的优先队列优化的Dijkstra算法。
原来版本有大量时间浪费在通过邻接矩阵找边和搜索当前最短路径中,而优化后的算法中,用结构体Edge存储边信息,省却了找有效边的麻烦。而且采用优先队列,可以快速找到最短路径,又省却一部分时间,所以在时间上具有极强的优越性。

原文链接:https://blog.csdn.net/Maybemust/article/details/77417270

该算法的基本思想是:

(1)设置两个顶点的集合S和T=V-S,集合S中存放已找到最短路径的顶点,集合T存放当前还未找到最短路径的顶点;

(2)初始状态时,集合S中只包含源点v0;

(3)从集合T中选取到某个顶点vi(要求vi到v0的路径长度最小)加入到S中;

(4)S中每加入一个顶点vi,都要修改顶点v0到T中剩余顶点的最短路径长度值,它们的值为原来值与新值的较小者,新值是vi的最短路径长度加上vi到该顶点的路径长度;

(5)不断重复(3)和(4),直到S包含全部顶点。

优先队列

  • 优先队列基础
  • 优先队列优化dijkstra
  • 优先队列优化dijkstra

斐波那契堆

  • 斐波那契堆(一)之 图文解析 和 C语言的实现

(4)算法流程

  1. 存图

将数据读取进来->根据任务数量和线程数分块->合并读取到的所有数据并排序计算节点的数量->将图里面所有的边的原始id全部映射为新的有序id->计算每一个节点在图中存放的位置,并统计节点的出入度->统计有哪些特殊节点(入度为0,初度为1),这样的点后面不需要去计算位置中心性

  1. dijkstra+优先队列+数组+vector 找最短路径

寻找以begin为起点到其它所有点的最短路径->维护一个优先队列和数组,原因:在Dijkstra算法中,要循环num_node-1次,每一次都要找出dis数组最小的节点,然后表明该节点已经找到了最优路径,那么再以这个节点为中间转折节点,然后去更新还没有找到最优路径的节点,这样的话,我们每次进入一个循环,就要寻找dis最小的位置(1~n),因为每次都取的为dis中最小的数值,所以将寻找最短路径和节点信息的步骤用优先队列来实现,数据类型为map(id,value),(节点,路径),这样经历一次dijkstra后,会得到每一个节点到其他节点的最短路径以及能到达的节点个数

  1. 计算位置中心性

根据dijkstra结果更新每一个节点的centrality的值(用公式),最后汇总每个节点的centrality

  1. 排序

排序centrality 输出前100

你可能感兴趣的:(2021秋招找工作准备)