Data structures 如何在Coq中实现联合查找(不相交集)数据结构?

Data structures 如何在Coq中实现联合查找(不相交集)数据结构?,data-structures,coq,disjoint-sets,union-find,Data Structures,Coq,Disjoint Sets,Union Find,我对Coq很陌生,但对于我的项目,我必须在Coq中使用union find数据结构。Coq中是否有union find(不相交集)数据结构的实现 如果没有,有人能提供一个实现或一些想法吗?它不必非常高效。(无需进行路径压缩或所有花哨的优化)我只需要一个能够容纳任意数据类型(或nat,如果太难)并执行以下操作的数据结构:union和find 提前感谢如果您只需要一个数学模型,而不考虑实际性能,我会选择最简单的一个:一个函数映射(有限部分函数),其中每个元素可以选择链接到另一个元素,并与之合并 如

我对Coq很陌生,但对于我的项目,我必须在Coq中使用union find数据结构。Coq中是否有union find(不相交集)数据结构的实现

如果没有,有人能提供一个实现或一些想法吗?它不必非常高效。(无需进行路径压缩或所有花哨的优化)我只需要一个能够容纳任意数据类型(或nat,如果太难)并执行以下操作的数据结构:unionfind


提前感谢

如果您只需要一个数学模型,而不考虑实际性能,我会选择最简单的一个:一个函数映射(有限部分函数),其中每个元素可以选择链接到另一个元素,并与之合并

  • 如果一个元素没有链接到任何东西,那么它的规范代表就是它自己
  • 如果一个元素链接到另一个元素,那么它的规范代表就是另一个元素的规范代表
注意:在这个答案的剩余部分,正如union find的标准一样,我将假设元素只是自然数。如果您想要其他类型的元素,只需使用另一个将所有元素绑定到唯一数字的映射即可

然后定义一个函数
find:UnionFind→ 纳特→ nat
返回给定元素的规范化代表,尽可能长时间地遵循链接。请注意,该函数将使用递归,其终止参数并非微不足道。为了实现这一点,我认为最简单的方法是保持一个数只链接到较小数的不变量(即,如果
I
链接到
j
,那么
I>j
)。然后递归终止,因为在跟踪链接时,当前元素是递减的自然数

定义函数
union:UnionFind→ 纳特→ 纳特→ UnionFind
更简单:
union m i j
只需返回一个更新的映射,其中
max i'j'
链接到
min i'j'
,其中
i'=查找m i
j'=查找m j

[关于性能的旁注:保持不变意味着您无法根据一对分区的列组适当地选择要合并到另一对分区中的哪一个;但是,如果需要,您仍然可以实现路径压缩!]

至于地图到底使用哪种数据结构:有几种可用的。 (在标题FSets下查看)有几个实现(FMapList、FMapPositive等)满足接口要求。 stdpp图书馆有

同样,如果性能不是问题,只需选择最简单的编码,或者更重要的是,选择使您的证明最简单的编码。我想到的只是一系列自然数。 列表的位置是按相反顺序排列的元素。 列表的值是偏移量,即为达到链接目标而向前跳过的位置数

  • 对于链接到
    j
    i>j
    )的元素
    i
    ),偏移量为
    i− j
  • 对于标准代表,偏移量为零
用我最好的伪ASCII艺术技巧,这里有一个链接为{6]的地图↦2, 4↦2, 3↦0, 2↦1}和规范代表是{5,1,0}:

  6   5   4   3   2   1   0   element
  ↓   ↓   ↓   ↓   ↓   ↓   ↓
               /‾‾‾‾‾‾‾‾‾↘
[ 4 ; 0 ; 2 ; 3 ; 1 ; 0 ; 0 ] map
   \       \____↗↗ \_↗
    \___________/

其动机是,上面讨论的不变量随后在结构上得到加强。因此,人们希望
find
实际上可以通过结构归纳(在列表的结构上)来定义,并且可以免费终止


相关文件如下:


它描述了一个高效的联合查找数据结构在ML中的实现,该结构从用户角度来看是持久的,但在内部使用了变异。你可能更感兴趣的是,他们在Coq中证明了它的正确性,这意味着他们有一个联合发现的Coq模型。然而,这个模型反映了他们试图证明正确的命令式程序的内存存储。我不确定它对您的问题有多适用。

Maëlan有一个很好的答案,但对于更简单、效率更低的不相交集数据结构,您可以使用函数来表示它们。这避免了任何终止粘性。本质上,任何总函数的前像在域上形成不相交集。另一种方法是将任何不相交集
G
表示为当前的应用程序
find\u root G:nat->nat
,因为
find\u root
是不相交集提供的基本接口。 这也类似于在Coq中使用函数表示地图,就像在软件基础中一样

您还可以使其在不相交集中保持的类型上具有泛型,如下所示

Definition ds (a : Type) := a -> nat.
Definition find_root {a} (g : ds a) x := g x.
Definition in_same_set {a} (g : ds a) x y := 
    eq_nat_decide (g x) (g y).
Definition union {a} (g : ds a) x y : ds a := 
    fun z =>
    if in_same_set g x z
    then find_root g y
    else find_root g z.
要初始化特定
a
的不相交集,基本上需要类型
a
的枚举实例

Definition init_bool_ds : ds bool := fun x => if x then 0 else 1.
您可能希望根据您的校对风格和需要,将
eq\u nat\u decision
替换为
eqb
或其他大致相同的东西

Definition init_bool_ds : ds bool := fun x => if x then 0 else 1.