快速功能归并排序

2023-11-26

这是我在 Scala 中实现的合并排序:

object FuncSort {
  def merge(l: Stream[Int], r: Stream[Int]) : Stream[Int] = {
    (l, r) match {
      case (h #:: t, Empty) => l
      case (Empty, h #:: t) => r
      case (x #:: xs, y #:: ys) => if(x < y ) x #:: merge(xs, r) else y #:: merge(l, ys)
    }
  }

  def sort(xs: Stream[Int]) : Stream[Int] = {
    if(xs.length == 1) xs
    else {
      val m = xs.length / 2
      val (l, r) = xs.splitAt(m)
      merge(sort(l), sort(r))
    }
  }
}

它工作正常,而且似乎渐进地也很好,但它比这里的 Java 实现慢得多(大约 10 倍)http://algs4.cs.princeton.edu/22mergesort/Merge.java.html并使用大量内存。是否有更快的合并排序实现功能性的?显然,可以逐行移植 Java 版本,但这不是我想要的。

UPD:我变了Stream to List and #:: to ::并且排序例程变得更快,仅比 Java 版本慢三到四倍。但我不明白为什么它不会因堆栈溢出而崩溃?merge不是尾递归,所有参数都经过严格评估......这怎么可能?


您提出了多个问题。我尝试按逻辑顺序回答这些问题:

Stream 版本中没有堆栈溢出

你并没有真正问这个问题,但它导致了一些有趣的观察。

在您使用的 Stream 版本中 #:: merge(...)在 - 的里面merge功能。通常这将是一个递归调用,并且可能会导致足够大的输入数据的堆栈溢出。但在本例中并非如此。运营商#::(a,b)实施于class ConsWrapper[A](有一个隐式转换)并且是同义词cons.apply[A](hd: A, tl: ⇒ Stream[A]): Cons[A]。正如您所看到的,第二个参数是按名称调用的,这意味着它是延迟计算的。

这意味着merge返回一个新创建的类型对象cons最终将再次调用合并。换句话说:递归不是发生在栈上,而是发生在堆上。通常你有足够的堆。

使用堆进行递归是处理非常深的递归的好技术。但它比使用堆栈慢得多。所以你用速度换取了递归深度。这是主要原因,为什么使用Stream太慢了。

第二个原因是,为了获取长度Stream,Scala 必须具体化整个Stream。但在排序过程中Stream无论如何,它都必须实现每个元素,所以这不会造成太大伤害。

List版本没有堆栈溢出

当您将 Stream 更改为 List 时,您实际上是在使用堆栈进行递归。现在可能会发生堆栈溢出。但对于排序,通常的递归深度为log(size),通常是底数的对数2。因此,要对 40 亿个输入项进行排序,您将需要大约 32 个堆栈帧。默认堆栈大小至少为 320k(在 Windows 上,其他系统具有更大的默认值),这为大量递归留下了空间,从而为大量输入数据进行排序。

更快的功能实现

这取决于 :-)

您应该使用堆栈,而不是堆进行递归。您应该根据输入数据决定您的策略:

  1. 对于小数据块,使用一些直接的算法对它们进行排序。算法的复杂性不会影响您,并且您可以通过将所有数据放入缓存中来获得大量性能。当然,你仍然可以手工代码分类网络对于给定的尺寸。
  2. 如果您有数字输入数据,则可以使用基数排序并将工作处理到处理器或 GPU 上的向量单元(更复杂的算法可以在GPU Gems).
  3. 对于中等大小的数据块,您可以使用分而治之的策略将数据拆分到多个线程(前提是您有多个核心!)
  4. 对于巨大的数据块,使用合并排序并将其拆分为适合内存的块。如果需要,您可以将这些块分布在网络上并在内存中排序。

不要使用交换并使用缓存。如果可以的话,使用可变数据结构并就地排序。我认为函数式排序和快速排序不能很好地结合在一起。为了使排序非常快,您必须使用有状态操作(例如,可变数组上的就地合并排序)。

我通常在我的所有程序上尝试这样做:尽可能使用纯函数式风格,但在可行的情况下对小部分使用有状态操作(例如,因为它具有更好的性能,或者代码只需要处理大量状态,并且在以下情况下变得更好可读)我用vars 而不是vals).

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

快速功能归并排序 的相关文章

  • Java时间转正常格式

    我有 Java 时间1380822000000 我想转换为我可以阅读的内容 import java util Date object Ws1 val a new Date 1380822000000 toString 导致异常 warnin
  • 并行化斐波那契序列生成器

    我正在学习并行化 在一项练习中 我得到了一些我应该提高性能的算法 其中之一是斐波那契数列生成器 array 0 0 array 1 1 for q 2 q lt MAX q array q array q 1 array q 2 我怀疑 这
  • 欧拉项目 45

    我还不是一名熟练的程序员 但我认为这是一个有趣的问题 我想我应该尝试一下 三角形 五边形 六边形 数字由以下生成 公式 三角形 T n n n 1 2 1 3 6 10 15 五边形 P n n 3n 1 2 1 5 12 22 35 六角
  • 具有通用返回类型的可选函数参数

    您将如何实现通过正则表达式解析某些输入并将创建的字符串转换为其他类型的类 我的做法是 class ARegex T regex Regex reform Option String gt T def findFirst input Stri
  • pytesseract 无法从图像中识别复杂的数学公式

    我在用pytesseractpython 中的模块 pytesseract从图像中识别文本 但它不适用于包含复杂数学公式 例如根 推导 积分数学问题或方程 的图像 代码2 py Import modules from PIL import
  • 贪心技术与穷举搜索有何不同?

    我正在为一些示例问题编写伪代码 并且我注意到贪婪技术和详尽搜索之间存在令人担忧的模式 Job 1 Job 2 Job 3 Job 4 Job 5 Person 1 9 2 7 8 Person 2 6 4 3 7 Person 3 5 8
  • 无法在 NetBeans 7.4rc1 上安装 nb-scala

    我已经安装了 NB 7 4rc1 并从下载了 nb scalahttp sourceforge net projects erlybird files nb scala http sourceforge net projects erlyb
  • 如何高效生成总和在指定范围内的所有组合(在所有深度)

    假设您有一组值 1 1 1 12 12 16 如何生成总和在预定义范围内的所有可能组合 不重复 min max 例如 这里是 所有深度的 范围在13 and 17 1 12 1 1 12 1 1 1 12 16 1 16 这假设具有相同值的
  • 压缩很多小字符串的算法?

    我正在寻找一种压缩小 ASCII 字符串的算法 它们包含大量字母 但也可以包含数字和很少的特殊字符 它们很小 平均约为 50 100 字节 最多 250 个字节 例子 Android show EditText setError above
  • 用于计算有向图上非循环路径数量的快速算法

    简而言之 我需要一个fast计算简单有向图中有多少条非循环路径的算法 By simple我的意思是没有自环或多重边的图 Apath可以从任何节点开始 并且必须在没有传出边的节点上结束 一条路径是acyclic如果没有边出现两次 我的图 经验
  • 比 BMH (Boyer–Moore–Horspool) 更快的算法

    您会使用哪种算法来搜索短文本中的短子字符串 简而言之 我的意思是子字符串有 5 10 个字符 字符串有 255 个字符 我正在考虑根据输入数据长度选择算法 哪种算法对于较长的输入更好 Try Turbo BM http www igm un
  • Scala:为什么 Actor 是轻量级的?

    是什么让演员如此轻盈 我什至不确定它们是如何工作的 它们不是单独的线程吗 当他们说轻量级时 他们的意思是每个参与者都没有映射到单个线程 JVM 提供共享内存线程 锁作为主要形式 并发抽象 但分享了 内存线程是相当重量级的 并招致严重的绩效处
  • 如何实现DataGridView的自动排序?

    我以编程方式将列添加到 DataGridView 然后绑定到列表 默认情况下 列的排序模式为自动 但是当我运行我的应用程序时 单击标题没有任何反应 向上 向下箭头未显示 从看MSDN来看 关于自动排序并没有太多说的 他们更详细地介绍了程序化
  • 为什么 sbt 在 build.sbt 工作时使用 Build.scala 报告“未找到:值 PlayScala”?

    我正在创建一个多模块 sbt 项目 其结构如下
  • 如何从数据框中按降序获取前n家公司

    我正在尝试从数据框中获取排名前 n 的公司 下面是我的代码 data Forbes2000 package HSAUR sort Forbes2000 profits decreasing TRUE 现在我想从这个排序向量中获取前 50 个
  • 如何在 ISO Prolog 中定义(和命名)相应的安全术语比较谓词?

    标准术语顺序 ISO IEC 13211 1 7 2 术语顺序 针对所有术语 包括变量 进行定义 虽然这有很好的用途 想想实施setof 3 这使得 8 4 术语比较中内置函数的许多其他干净且合乎逻辑的使用成为声明式噩梦 到处都是 imps
  • 在 Scala 中设计方便的默认值映射

    我发现自己使用了很多嵌套映射 例如 Map Int Map String Set String 并且我希望在访问新密钥时自动创建新的 Map Set 等 例如 像下面这样 val m m 1992 foo bar 请注意 如果不需要 我不想
  • Scala:var List 与 val MutableList

    在 Odersky 等人的 Scala 书中 他们说使用列表 我还没有从头到尾读过这本书 但所有的例子似乎都使用了 val List 据我了解 还鼓励人们使用 vals 而不是 vars 但在大多数应用程序中 使用 var List 或 v
  • 如何使用 Rrank() 函数创建新的ties.method? [复制]

    这个问题在这里已经有答案了 我试图按人口和日期排序这个数据框 所以我使用order and rank 功能 gt df lt data frame idgeoville c 5 8 4 3 4 5 8 8 date c rep 1950 4
  • 寻找簇的中心

    我有以下问题 进行抽象以找出关键问题 我有 10 个点 每个点与其他点有一定距离 我想要 能够找到簇的中心 即与其他点的成对距离最小的点 令 p j p k 表示点 j 和 k 之间的成对距离p i 是簇的中心点 iff p i s t m

随机推荐