Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sorting 功能性风格中最快的排序_Sorting_Functional Programming - Fatal编程技术网

Sorting 功能性风格中最快的排序

Sorting 功能性风格中最快的排序,sorting,functional-programming,Sorting,Functional Programming,作为一名本科生,我学习了O(n logn)排序算法,并证明在一般情况下,当我们所能做的只是比较两个数字时,我们不能做得更好。这是一个随机存取存储器的计算模型。 我想知道函数式(引用透明)程序是否存在这样的理论下限。让我们假设每一个beta减少都算作一个步骤,每一个比较都算作一个步骤。我认为现代函数式编程实现(至少是Clojure,因为这是我所知道的唯一一个)确实有不可变的内存,但这并不意味着改变列表和类似的结果会复制整个原始列表。因此,我不认为使用命令式或函数式习惯用法实现排序算法之间存在数量级

作为一名本科生,我学习了O(n logn)排序算法,并证明在一般情况下,当我们所能做的只是比较两个数字时,我们不能做得更好。这是一个随机存取存储器的计算模型。
我想知道函数式(引用透明)程序是否存在这样的理论下限。让我们假设每一个beta减少都算作一个步骤,每一个比较都算作一个步骤。

我认为现代函数式编程实现(至少是Clojure,因为这是我所知道的唯一一个)确实有不可变的内存,但这并不意味着改变列表和类似的结果会复制整个原始列表。因此,我不认为使用命令式或函数式习惯用法实现排序算法之间存在数量级的计算差异。

有关如何在不复制内存的情况下修改不可变列表的详细信息:

作为一个例子,这可能是如何工作的,请考虑Culjress引用中的这个片段:

…在Clojure中,所有标量和核心数据结构都是这样的。他们 是价值观。它们是不变的

地图
{:name“John”:hit points 200:super power:resourcess}
是一个 价值如果你想“改变”约翰的生命值,你不会改变 任何东西本身,但更确切地说,你只是变出了一个全新的hashmap 价值观

但请稍候:如果您使用类似C的命令式编程 语言,这听起来是疯狂的浪费。然而,阴阳却对这一点产生了影响 不可改变的是,在幕后,克洛朱尔也分享了这一点 数据结构。它跟踪所有的碎片并重复使用它们 无处不在。例如,如果您有一个1000000项列表,并且希望 再加上一件,你只要告诉Clojure,“给我一件新的。” 但是加上这个项目“---Clojure会尽职尽责地还给你一个 1000001项目清单,无时间限制。在你不知情的情况下,它正在使用 原始列表

那么,为什么要对不变性大惊小怪呢? 我不知道函数式编程的完整历史,但在我看来,函数式语言的不变性本质上是将复杂的共享内存抽象出来的


虽然这很酷,但如果没有一个语言管理的共享数据结构作为整个机制的基础,那么对于许多用例来说,它的速度将是不切实际的慢。

因此,让我们假设您同意这样一个证据,即在我们只有比较的一般情况下,我们不能做得比(n*logn)更好

所以问题是我们是否能证明我们也能做到同样的事情而没有副作用

一种方法是,如果你可以在O(n*logn)中构建一个不可变的二叉搜索树,然后中缀遍历它(可以在O(n)中完成),那么我们就有了一个排序算法

如果我们能够遍历所有项并将每个项添加到O(logn)中的一个平衡的不可变(持久)树中,它将为我们提供O(n*logn)算法

我们可以添加到O(logn)中的持久二叉树中吗?当然,在持久性数据结构库的每个合理实现中,都有不可变的平衡二叉搜索树,插入O(logn)

要想知道为什么它是可能的,想象一个标准的平衡二叉搜索树,例如红黑树。通过遵循与可变节点相同的算法,可以使其成为不可变的版本,除非指针或颜色发生变化时,需要将新节点及其所有父节点分配给根节点(同时在必要时转换它们)。不会更改的分支会被重用。受影响的节点最多为O(logn),因此每次插入最多为O(logn)操作(包括分配)。如果您知道红-黑,您可以看到除了常数之外没有其他乘数(对于旋转,您可以为受影响的兄弟姐妹获得一些额外的分配,但这仍然是一个常数因子)


这个非常非正式的演示可以让您知道,存在一个O(n*logn)排序的证明,它没有副作用。然而,我还遗漏了一些东西。例如,这里的分配被认为是O(1),这可能并不总是如此,但这会变得太复杂。

是的,快速排序,即O(n lg n)在函数式编程中是可能的(而且非常简单)。@Gabe我发现更容易实现和推理-包括确定[下限],特别是因为每个子问题(读:递归步骤)将问题一分为二。Mergesort的时间复杂度为O(n lg n),但不同实现之间的空间复杂度不同。@user2864740我相信“函数式”在这种情况下,我们只是在编写引用透明的代码。众所周知,您总是可以在纯代码中模拟可变内存,其开销系数为
logn
,并且在某些特定问题上,您无法做得更好(有关更多详细信息,请参阅此处:),因此,一个算法的时间复杂度在函数风格上可能与一般算法不同。谢谢@Ben,我将更新question@user2864740:并非所有排序算法都可以用函数式编写,并且仍然具有相同的时间复杂度。函数式编程风格意味着在赋值后不更改任何变量,即g你不能在数组创建后修改它的单个元素。谢谢jkschnieder。我会看看你的clojure链接。一般来说,我同意函数语言的基础求值器可以很智能,避免不必要的复制。但是,使用函数语言有很多很好的理由,特别是在定理证明中。Hoa对于程序正确性的形式化推理来说,re-Logic远不如依赖类型好。Clojure赞不绝口——但它是否以某种方式回答了这个问题?@Ingo——计算复杂性差异的唯一明显原因