图论是我最喜欢的数学和计算科学主题,我将在这篇博文中介绍图的代数我已经努力了一段时间。代数已经成为我操作图形的工具,我希望你也会发现它很有用。
这项工作的根源可以追溯到我在2009年CONCUR会议上提交的文件,该文件被正确地拒绝了。随后,我发表了一些针对具体应用的论文,逐渐提高了我对代数的理解。最全面的描述可以在ACM TECS公司(有预印本在这里). 在这里,我将对图代数的最简单版本进行一般介绍,并展示如何在Haskell中实现它。
更新:这一系列博客文章是作为功能珍珠在2017年哈斯克尔研讨会上。
构建图形
让G公司是一组顶点来自固定宇宙的图。例如,我们可以考虑顶点为正整数的图。图g∈g可以用一对表示(V、E)其中V是其顶点集,E⊆V×V是其边集。
最简单的可能图形是空的图表。我将用以下方式表示ε公式和依据空的
在Haskell代码中。因此,ε=(∅,∅)和ε∈G。
带有单个顶点v将简单地表示为v(v)例如,1∈G是具有单个顶点1的图,即({1},∅)。在哈斯克尔我会用顶点
将给定的顶点提升为图的类型。
为了从上述原语构造更大的图,我将使用两个二进制运算符覆盖和连接,分别用+和→表示。两个图形的叠加+定义为:
(五)1,E1)+(V2,E2)=(V1(V)2,E1“E2)
换句话说,两个图的重叠只是它们的顶点和边的并集。connect→的定义类似:
(五)1,E1)→(V2,E2)=(V1(V)2,E1“E2(V)1×V2)
不同的是,当我们连接两个图时,我们从左参数中的每个顶点向右参数中的各个顶点添加一条边。以下是几个示例:
- 1+2是具有两个孤立顶点1和2的图。
- 1→2是顶点1和2之间具有有向边的图。
- 1→(2+3)是具有三个顶点{1,2,3}和两条有向边(1,2)和(1,3)的图。在哈斯克尔我们可以写作
连接1(覆盖2 3)
.
- 1→1是带有顶点1和a的图自循环(从顶点到自身的边)。
以下类型类在Haskell中表达了上述内容:
班 图表 克 哪里
类型 顶点克空的 :: 克
顶点 :: 顶点 克 -> 克
覆盖 :: 克 -> 克 -> 克
连接 :: 克 -> 克 -> 克
让我们构造一些图!包含给定的未连接顶点列表的图可以按如下方式构造:
顶点 :: 图表 克 =>[顶点 克]-> 克顶点= 文件夹覆盖为空. 地图顶点
这里有一个集团给定顶点列表上的(完全连通图):
集团 :: 图表 克 =>[顶点 克]-> 克集团= 文件夹连接为空. 地图顶点
例如,集团[1..]
是所有正整数上的无限团;我们将把这种派系称为覆盖整个宇宙的派系完全图.我们也可以构造任何给定其边缘主义者:
从边缘列表 :: 图表 克 =>[(顶点 克,顶点 克)]-> 克从边缘列表= 文件夹覆盖为空. 地图边缘哪里边缘(x,年)=顶点x`连接`顶点y
正如我们将在下一节中看到的那样,图满足几个定律,并形成一个与半环.
代数结构
上面介绍的结构(G,+,→,ε)满足许多常见的定律:
- (G,+,ε)是幂等可换的幺半群
- (G,→,ε)是幺半群
- → 分布在+上,例如1→(2+3)=1→2+1→3
以下内容分解公理,是使图代数不同于半环的唯一定律:
x→y→z=x→y+x→z+y→z
实际上,在半环中,这两个算子有不同的单位元,让我们将它们表示为ε+和ε→分别是。通过使用分解公理,我们可以证明它们是一致的:
ε+ |
= |
ε+→ ε→→ ε→ |
(→的标识) |
|
= |
ε+→ ε→ + ε+→ ε→ + ε→→ ε→ |
(分解) |
|
= |
ε+ + ε+ + ε→ |
(→的标识) |
|
= |
ε→ |
(+的标识) |
+的幂等性也遵循分解公理。
以下是描述图代数的最小公理集:
- +是交换的和结合的
- (G,→,ε)是幺半群,即→是结合的,ε是单位元
- → 分布在+
- → 可以分解为:x→y→z=x→y+x→z+y→z
读者的练习:从上述最小公理集证明ε是+的恒等式。这可不是小事!还证明了+是幂等的。
注意,要从有向图切换到无向图,添加→的交换性公理就足够了。我们将在中对此进行探讨未来的博客帖子.
示例
让我们看一下图表类型满足上一节中的规则的类。第一个叫关系,采用我们基于集合的覆盖和连接操作符定义,因此是自由的实例(即不符合任何其他法律):
数据 关系一= 关系{域:: 设置一,关系:: 设置(a),a) }衍生(等式,显示)实例 订单 一 => 图表(关系 一)哪里
类型 顶点(关系a)=一空的= 关系 设置.空的设置.空的顶点x= 关系(设置.单例x)设置.空的覆盖x y= 关系(域x`设置.联合`domain y)(关系x`设置.并集`关系y)连接x y= 关系(域x`设置.联合`domain y)(关系x`设置.并集`关系y`设置.工会`设置.来自DistinctAscList[(a),b)|一<- 设置.元素(域x),b条<- 设置.元素(域y)])
让我们也做关系的实例号码键入class,以便使用+和*运算符。
实例(订单 一,号码 一)=> 号码(关系 一)哪里
from整数 =顶点. from整数
(+) =覆盖(*)=连接符号 = 常数空的防抱死制动系统 = 身份证件
否定 = 身份证件
注:号码法律abs x*符号x==x
满足,因为x→ε=x。事实上,任何图表实例可以成为号码实例,如果需要的话。我们现在可以使用交互式GHC玩图形了:
λ> 1* (2 + 三):: 关系 国际
关系{域=来自列表[1,2,三],关系=来自列表[(1,2),(1,三)]}λ> 1* (2 + 三)+ 2*三 ==(集团[1..三]:: 关系 国际)真的
通过将所有图构造函数嵌入到基本代数数据类型中,可以获得另一个简单的实例:
数据 基本一= 清空
| 顶点一| 覆盖(基本a)(基本a)| 连接(基本a)(基本a)衍生 显示
实例 图表(基本 一)哪里
类型 顶点(基本a)=一空的= 清空顶点= 顶点覆盖= 覆盖连接= 连接
我们不能使用派生的等式例如,因为这显然违反了代数定律。覆盖空空
在结构上与清空
。但是,我们可以实现自定义等式实例如下:
实例 订单 一 => 等式(基本 一)哪里x个==年=到关系x==到关系y哪里
到关系 :: 订单 一 => 基本 一 -> 关系 一到关系=折叠基础折叠基础 ::(顶点 克~一,图表 克)=> 基本 一 -> 克折叠基础清空 =空的折叠基础(顶点x)=顶点x折叠基础(覆盖x年)=覆盖(折叠基本x)(折叠基本y)折叠基础(连接x年)=连接(折叠基本x)(折叠基本y)
这个基本实例很有用,因为它可以更紧凑地表示紧密连接的图。例如,集团[1.n]::基本智力
在内存中具有线性大小表示,而集团[1.n]::关系Int
分别存储每条边,因此O(n)2)内存。正如我将在未来的博客文章中演示的那样,我们可以利用紧凑的图表示来推导算法,这些算法在稠密图上的速度比在边缘专家上运行的现有图算法更快。
总结
几年来,我在许多不同的项目中使用了上述图形代数,发现它非常有用。我将介绍一些代数的味道后续博客帖子允许处理无向图、传递闭图(也称为部分订单或依赖图)、图形族及其各种组合。代数的所有这些风格都可以通过扩展公理集来获得。
我在哈斯克尔图书馆工作藻类实现图的代数,并打算很快发布。如果您对如何改进上述代码片段有任何建议,请告诉我。