【问题标题】:Is it better to call sort descending or just to sort a list and reverse it?调用降序排序还是仅对列表进行排序并反转它更好?
【发布时间】:2017-09-22 23:18:41
【问题描述】:

List 模块包含sortDescending 函数,可将列表从高到低排序。我读到对列表进行排序然后反转它会更快。于是我试了一下,好像是这样的。

 [1.0..1000000.0] |> List.sortDescending;;
Real: 00:00:00.322, CPU: 00:00:00.328, GC gen0: 10, gen1: 4, gen2: 0

[1.0..1000000.0] |> List.sort |> List.rev;;
Real: 00:00:00.243, CPU: 00:00:00.250, GC gen0: 15, gen1: 7, gen2: 0

(我试了很多次,这些值很典型)

是否有任何理由使用sortDescending 而不是仅仅对列表进行排序然后反转结果?

【问题讨论】:

  • 我会在这里使用随机测试进行测试 - 这里的输入已经排序,所以你会期望 sort then rev 会更快,但对于未排序的数据可能不是这样。
  • 我尝试了随机浮点数并得到了相同的速度差异。

标签: list sorting f# reverse


【解决方案1】:

目前,排序和反转应该只对没有标识的事物(如整数和浮点数)更快。如果您查看 github 上的源代码(参见 list.fs 和 local.fs),都将列表复制到数组中,对数组进行排序,然后重新创建列表。

当您使用sortDescending 时,它使用stableSortInPlaceWith 对数组进行排序,它始终使用stableSortWithKeysAndComparer。此功能在排序后复制结果并调整重复项以使其处于原始顺序。这是stable sort,所以重复的项目不会移动。

排序使用stableSortInPlace,它有一个优化来检测像没有身份的浮动这样的东西,所以不需要使用稳定的排序;没有第二个副本使它更快。如果它是一个对象列表,List.sort 然后使用stableSortWithKeysAndComparer,所以它应该与 List.sortDescending 相同的速度,但由于相反,它会变慢。对于我的测试来说,速度较慢,但​​很难确定,因为我的虚拟机位于共享基础架构上,并且给出了截然不同的结果。

 [1.0..1000000.0] |> List.map Some |> List.sortDescending;;
Real: 00:00:13.616, CPU: 00:00:13.275, GC gen0: 146, gen1: 12, gen2: 5

 [1.0..1000000.0] |> List.map Some |> List.sort |> List.rev;;
Real: 00:00:17.727, CPU: 00:00:17.316, GC gen0: 149, gen1: 15, gen2: 6

正如 cmets 中所指出的,列表已经排序,这使得 List.sort 更快(最后我看到,.NET 对数组使用了快速排序,如果已经排序,则速度很快)。但是,即使您先反转列表,List.sort |> List.rev 在作为双精度列表时仍然比 List.sortDescending 快。

还有一点需要注意的是,这两者实际上并不是在所有情况下都等价的。因为是稳定排序,所以List.sortDescending之后的重复项会按照相同的顺序排列,但是会被List.sort |> List.rev反转。

【讨论】:

    猜你喜欢
    • 2016-05-08
    • 1970-01-01
    • 2016-10-18
    • 1970-01-01
    • 2022-01-06
    • 2021-02-03
    • 1970-01-01
    • 2011-07-22
    相关资源
    最近更新 更多