flink 使用Transitive Closure算法实现可达路径查找。

1、Transitive Closure是翻译闭包传递?我觉得直译不准确,意译应该是传递特性直至特性关闭,也符合本例中传递路径,寻找路径可达,直到可达路径不存在(即关闭)。

2、代码很简单,里面有些概念直指核心原理,详细看注释。

/**
* @Author: xu.dm
* @Date: 2019/7/3 11:41
* @Version: 1.0
* @Description: 传递闭包算法,本例中就是根据成对路径,查找和生成新的可达路径
* 例如:1-2,2-4这两对数据,可以得出新的可达路径1-4。
*
* 迭代算法步骤:
* 1、获取成对数据集edges,里面包括路径对,比如 1->2,2->4,2->5等,如果是无向边,还可以反转数据集union之前的数据。本例按有向边处理
* 2、生成迭代头paths可迭代数据集
* 3、用paths和原始数据集edges做join连接,找出头尾相连的数据nextPaths,即类似1->2,2->4这种,然后生成新的路径1->4。
* 4、新的路径集nextPaths和迭代头数据集paths进行并集操作,即union操作,生成新的nextPaths,这个时候它包含了新旧两种数据
* 在这里总是nextPaths>=paths
* 5、去重操作,第一次迭代不会重复,但是第二次迭代开始就会有重复数据,通过groupBy全字段,去分组第一条即可达到去重效果
* 6、以上核心迭代体完成,后面需要形成迭代闭环,确定迭代退出条件
* 7、退出原理:每次迭代完成后,需要检查是否新的路径产生,如果没有则表示迭代可以结束
* 8、可达寻路步骤完成后,通过对比nextPaths和paths,如果nextPaths>paths,表示有新路径生成,需要继续迭代,直到nextPaths=paths
* 9、这里有一个迭代重要的概念,paths和nextPaths是通过迭代闭环不断更新的
* 10、本例中迭代头和迭代尾的数据流向:paths->nextPaths->paths.
* 11、本例通过bulk迭代方式实现了delta迭代的效果
**/
public class TransitiveClosureNaive {
public static void main(String args[]) throws Exception {
// Checking input parameters
final ParameterTool params = ParameterTool.fromArgs(args); // set up execution environment
ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); // make parameters available in the web interface
env.getConfig().setGlobalJobParameters(params); final int maxIterations = params.getInt("iterations", 10); DataSet<Tuple2<Long, Long>> edges;
if(params.has("edges")){
edges = env.readCsvFile(params.get("edges")).fieldDelimiter(" ").types(Long.class, Long.class);
}else {
System.out.println("Executing TransitiveClosureNaive example with default edges data set.");
System.out.println("Use --edges to specify file input.");
edges = ConnectedComponentsData.getDefaultEdgeDataSet(env);
} IterativeDataSet<Tuple2<Long,Long>> paths = edges.iterate(maxIterations); DataSet<Tuple2<Long,Long>> nextPaths = paths
.join(edges)
.where(1)
.equalTo(0)
.with(new JoinFunction<Tuple2<Long, Long>, Tuple2<Long, Long>, Tuple2<Long, Long>>() {
/**
left: Path (z,x) - 通过z可达x
right: Edge (x,y) - 通过x可达y
out: Path (z,y) - 最终输出z可达y
*/
@Override
public Tuple2<Long, Long> join(Tuple2<Long, Long> left, Tuple2<Long, Long> right) throws Exception {
return new Tuple2<>(left.f0,right.f1);
}
})
//类似withForwardedFieldsFirst这种无损转发语义声明,是可选项,有助于提高flink优化器生成更高效的执行计划
//转发第一个输入Tuple2<Long, Long>中的第一个字段,转发第二个输入Tuple2<Long, Long>中的第二个字段
.withForwardedFieldsFirst("0").withForwardedFieldsSecond("1")
//合并原有的路径
.union(paths)
//这里的groupBy两个fields是打算给reduceGroup去重使用
.groupBy(0,1)
.reduceGroup(new GroupReduceFunction<Tuple2<Long, Long>, Tuple2<Long, Long>>() {
@Override
public void reduce(Iterable<Tuple2<Long, Long>> values, Collector<Tuple2<Long, Long>> out) throws Exception {
out.collect(values.iterator().next());
}
})
.withForwardedFields("0;1"); //对比paths以及新生成的nextPaths,获取nextPaths中比paths多的路径
//从上面的算子可以得知,nextPaths总是大于或等于paths
DataSet<Tuple2<Long,Long>> newPaths = paths
.coGroup(nextPaths)
.where(0).equalTo(0)
.with(new CoGroupFunction<Tuple2<Long, Long>, Tuple2<Long, Long>, Tuple2<Long, Long>>() {
Set<Tuple2<Long, Long>> prevSet = new HashSet<>();
@Override
public void coGroup(Iterable<Tuple2<Long, Long>> prevPaths, Iterable<Tuple2<Long, Long>> nextPaths, Collector<Tuple2<Long, Long>> out) throws Exception {
for(Tuple2<Long,Long> prev:prevPaths){
prevSet.add(prev);
}
//检查有没有新的数据产生,如果有就继续迭代,否则迭代终止
for(Tuple2<Long,Long> next:nextPaths){
if(!prevSet.contains(next)){
out.collect(next);
}
}
}
}).withForwardedFieldsFirst("0").withForwardedFieldsSecond("0"); //迭代尾,在这里形成闭环,nextPaths是反馈通道,nextPaths数据集被重新传递到迭代头paths里,然后通过迭代算子不断执行。
//newPaths为空或者迭代达到最大次数,迭代结束。newPaths这里表示是否有新的路径。
//数据集迭代环:paths->nextPaths->paths
DataSet<Tuple2<Long, Long>> transitiveClosure = paths.closeWith(nextPaths, newPaths); // emit result
if (params.has("output")) {
transitiveClosure.writeAsCsv(params.get("output"), "\n", " "); // execute program explicitly, because file sinks are lazy
env.execute("Transitive Closure Example");
} else {
System.out.println("Printing result to stdout. Use --output to specify output path.");
transitiveClosure.print();
}
}
}

3、原始数据

public class ConnectedComponentsData {
public static final long[] VERTICES = new long[] {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; public static DataSet<Long> getDefaultVertexDataSet(ExecutionEnvironment env) {
List<Long> verticesList = new LinkedList<Long>();
for (long vertexId : VERTICES) {
verticesList.add(vertexId);
}
return env.fromCollection(verticesList);
} public static final Object[][] EDGES = new Object[][] {
new Object[]{1L, 2L},
new Object[]{2L, 3L},
new Object[]{2L, 4L},
new Object[]{3L, 5L},
new Object[]{6L, 7L},
new Object[]{8L, 9L},
new Object[]{8L, 10L},
new Object[]{5L, 11L},
new Object[]{11L, 12L},
new Object[]{10L, 13L},
new Object[]{9L, 14L},
new Object[]{13L, 14L},
new Object[]{1L, 15L},
new Object[]{16L, 1L}
}; public static DataSet<Tuple2<Long, Long>> getDefaultEdgeDataSet(ExecutionEnvironment env) { List<Tuple2<Long, Long>> edgeList = new LinkedList<Tuple2<Long, Long>>();
for (Object[] edge : EDGES) {
edgeList.add(new Tuple2<Long, Long>((Long) edge[0], (Long) edge[1]));
}
return env.fromCollection(edgeList);
} }
 

flink Transitive Closure算法,实现寻找新的可达路径的更多相关文章

  1. 启发式搜索A-Star算法 【寻找 最短路径 算法】【地理几何位置 可利用的情况】

    在处理最短路径问题时,有一种启发式算法是我们应该了解的,由于其有着优秀的探索效率在各自现实项目中多有应用,它就是 A-star 算法,或  A*  算法. 个人观点: A*  算法并不保证找到的路径一 ...

  2. 我在阿里这仨月 前端开发流程 前端进阶的思考 延伸学习的方式很简单:google 一个关键词你能看到十几篇优秀的博文,再这些博文中寻找新的关键字,直到整个大知识点得到突破

    我在阿里这仨月 Alibaba 试用期是三个月,转眼三个月过去了,也到了转正述职的时间.回想这三个月做过的事情,很多很杂,但还是有重点. 本文谈一谈工作中遇到的各种场景,需要用到的一些前端知识,以及我 ...

  3. 实现一个算法,寻找字符串中出现次数最少的、并且首次出现位置最前的字符 如"cbaacfdeaebb",符合要求的是"f",因为他只出现了一次(次数最少)。并且比其他只出现一次的字符(如"d")首次出现的位置最靠前。

    实现一个算法,寻找字符串中出现次数最少的.并且首次出现位置最前的字符如"cbaacfdeaebb",符合要求的是"f",因为他只出现了一次(次数最少).并且比其 ...

  4. [经典算法题]寻找数组中第K大的数的方法总结

    [经典算法题]寻找数组中第K大的数的方法总结 责任编辑:admin 日期:2012-11-26   字体:[大 中 小] 打印复制链接我要评论   今天看算法分析是,看到一个这样的问题,就是在一堆数据 ...

  5. Java实现 蓝桥杯 算法训练 寻找数组中最大值

    算法训练 寻找数组中最大值 时间限制:1.0s 内存限制:512.0MB 提交此题 问题描述 对于给定整数数组a[],寻找其中最大值,并返回下标. 输入格式 整数数组a[],数组元素个数小于1等于10 ...

  6. cb41a_c++_STL_算法_填充新值fill_generate

    cb41a_c++_STL_算法_填充新值fill_generatefill(b,e,v)fill_n(b,n,v),填充n个vgenerate(b,e,p)generate_n(b,n,p) gen ...

  7. php glob()函数实现目录文件遍历与寻找与模式匹配的文件路径

    采用PHP函数glob实现寻找与模式匹配的文件路径,主要讨论glob()函数的作用和用法,利用glob函数读取目录比其它的要快N倍,因为glob函数是内置函数处理起来自然要快. 一,函数原型 arra ...

  8. 更新索引库: $locate string 寻找包含有string的路径: $updatedb

    更新索引库: $locate string 寻找包含有string的路径: $updatedb 与find不同,locate并不是实时查找.你需要更新数据库,以获得最新的文件索引信息.

  9. dijkstra算法:寻找到全图各点的最短路径

    dijkstra算法介绍:即迪杰斯特拉算法,是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题.迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止,是一种广度优先 ...

随机推荐

  1. lvm - Logical Volume Manager - 逻辑卷管理

    下午突然感觉 lvm 相关的知识忘记了,恰好机房里的fedora服务器上 挂了4个500GB的HDD 硬盘没有使用,就拿来操作了一番: 下面有几篇关于lvm不错的文章,进行了链接,网上也有很多不错的博 ...

  2. [视频教程] ubuntu系统下安装最新版PHP7.3.X环境

    视频地址: https://www.bilibili.com/video/av69088870/ 笔记: 先安装一下这个命令 add-apt-repositoryapt-get install sof ...

  3. 1、mongoDB服务器的搭建与连接

    下载----编译----安装之后: 1.首先,创建一个mongodb_simple的目录,进入到目录中. 2.创建文件夹:data,用来存储数据库的数据文件. 3.创建文件夹:log,用来存储数据库的 ...

  4. 28.Java基础_抽象类

    抽象类的成员特点 public abstract class Animal { private String name; private int age; public Animal() { } pu ...

  5. Dijkstra算法堆优化详解

    DIJ算法的堆优化 DIJ算法的时间复杂度是\(O(n^2)\)的,在一些题目中,这个复杂度显然不满足要求.所以我们需要继续探讨DIJ算法的优化方式. 堆优化的原理 堆优化,顾名思义,就是用堆进行优化 ...

  6. appium--python启动appium服务

    前戏 前面我们都是在cmd下通过输入appium加端口号来启动服务的,在我们做自动化的时候,我们当然不希望我们手动启动appium服务,而是希望通过脚本自动启动appium服务. 我们可以使用subp ...

  7. 纯CSS打造BiliBili样式博客主题

    前言 一直以来,我都在思考如何减少不必要的JS代码,仅通过CSS来实现博客园主题美化.CSS有很多魔法代码,例如:before,iconfont,order,等等,利用好这些技巧,也能实现很好美化效果 ...

  8. ES6中有关数组的一些新操作

    1.Array.isArray() 用于确定传递的值是否是一个 Array. Array.isArray([1, 2, 3]); // true Array.isArray({foo: 123}); ...

  9. influxdb安装和学习

    安装 https://docs.docker.com/samples/library/influxdb/ 先启动,创建admin用户 docker run -d --name influxdb -p ...

  10. Spring Boot整合Mybatis配置详解

    首先,你得有个Spring Boot项目. 平时开发常用的repository包在mybatis里被替换成了mapper. 配置: 1.引入依赖: <dependency> <gro ...