【问题标题】:Which general purpose sorting algorithm does Swift use? It does not perform well on sorted dataSwift 使用哪种通用排序算法?它在排序数据上表现不佳
【发布时间】:2017-04-23 04:50:48
【问题描述】:

我一直在挑选和探索 Swift 标准库 sort() 函数的 Array 类型。令我惊讶的是,我注意到它在已经排序的数据上表现不佳。

Int 的数组进行排序似乎比对已经排序的相同数组进行排序要快 5 倍。对一个随机对象数组进行排序比对已经按排序顺序排序的相同对象快约 4 倍(排序对象数组与 Int 数组使用不同的算法,我确信因此我对两者都进行了排序以消除偏差)。

这些是结果:

Shuffled Int array sort time: 1.3961209654808
Shuffled ColorObject array sort time: 3.14633798599243
NOnshuffled Int array sort time: 7.34714204072952
NOnshuffled ColorObject array sort time: 10.9310839772224

下面是我的代码供参考:

class ElapsedTimer {

    let startTime: CFAbsoluteTime
    var endTime: CFAbsoluteTime?

    init() {
        startTime = CFAbsoluteTimeGetCurrent()
    }

    func stop() -> CFAbsoluteTime {
        endTime = CFAbsoluteTimeGetCurrent()
        return duration!
    }

    var duration: CFAbsoluteTime? {
        if let endTime = endTime {
            return endTime - startTime
        } else {
            return nil
        }
    }
}

public class CountedColor {
    public private(set) var count: Int
    public private(set) var color: UIColor

    public init(color: UIColor, colorCount: Int) {
        self.count = colorCount
        self.color = color
    }
}

var distributedIntArray = [Int]()

for value in 1..<1000000 {
    distributedIntArray.append(value)
}

var distributedCountedColorArray = distributedIntArray.map{ CountedColor(color: UIColor.white, colorCount: $0) }

distributedCountedColorArray.shuffle()
distributedIntArray.shuffle()

var timer = ElapsedTimer()
distributedIntArray.sort()
print("Shuffled Int array sort time: \(timer.stop())")
timer = ElapsedTimer()

distributedCountedColorArray.sort{ return $0.count < $1.count }
print("Shuffled Color array sort time: \(timer.stop())")
timer = ElapsedTimer()

distributedIntArray.sort()
print("NOnshuffled Int array sort time: \(timer.stop())")
timer = ElapsedTimer()

distributedCountedColorArray.sort{ return $0.count < $1.count }
print("Non shuffled Color array sort time: \(timer.stop())")

我的数组shuffle() 方法是从this post 中提取的。我的ElapsedTimer 只是简单地包装和使用CACurrentMediaTime() 函数。

我的问题是为什么我会看到这种行为?特别是当我对肯定应该使用通用排序的对象数组进行排序时。 swift使用什么样的通用排序算法?它肯定不可能是最坏情况和平均情况像 mergeSort 一样的情况。

【问题讨论】:

  • 问“为什么” Swift 选择某个实现并不是一个好的客观问题——基本原理问题很少做,因为通常很难找到客观的支持证据。去掉多余的东西会使问题更整洁。客观问题是“Swift 使用什么排序算法?(可以预期什么性能(在 xyz 情况下)?)”
  • 我相信问“为什么”是一个完全有效的问题。理解为什么选择一个实现同样重要,甚至更多,所有的决定都应该是合理的。话虽如此,我可以重新提出这个问题。
  • Swift 是开源的,所以如果你真的想知道他们使用的是什么排序算法,你为什么不去看看呢?至于您的实际问题,“为什么会”不适合 Stack Overflow;苹果为什么这么做或那是一个见仁见智的问题。
  • 什么是CountedColor
  • @YuchenZhong 让我编辑问题/代码 sn-p 以包含它

标签: arrays swift performance sorting time-complexity


【解决方案1】:

这似乎是这个问题的重复:Swift sorting algorithm implementation

此外,它在 shuffle 时表现如此出色的原因可能只是因为当它 shuffle 时它的性能没有达到 NlogN 的上限。对排序后的数组进行排序可能会更接近上限,所以它仍然是相同的排序。但我不知道这只是理论

【讨论】:

  • 这是不正确的,swift对一个已经排序的数组大于几个向下元素的排序性能是O(n**2)。在 shuffled 数组上,它归结为 O(n*log(n))
  • 按 O(n**2) 我假设您的意思是 N 平方。 swifts排序怎么可能是intro排序,而introSorts最坏情况复杂度是NlogN。
  • 正如 OP 的问题所证明的那样,swift 的排序似乎不像正确的 introsort,否则它不会容易受到有序输入的影响。也许深度阈值设置得非常高,它主要表现出快速排序行为。
  • 可能或枢轴选择不佳。
  • 枢轴选择只会改变排序容易受到哪种模式输入的影响。虽然我上次听说,swift 使用的是第一个元素,考虑到简单排序输入的常见程度,这似乎是一个非常糟糕的选择……
【解决方案2】:

Swift 使用Introsort。查看source code,我们看到选择的枢轴是第一个元素。 Introsort 上的维基百科页面说:

(...),关键操作之一是选择枢轴: 列表被分区的元素。最简单的支点 选择算法是取第一个或最后一个元素 list 作为枢轴,导致 sorted 或 几乎排序的输入。

因此,对于已排序的输入,Swift 的排序性能最差是完全可以预测的。

我为想要轻松重现 OP 声明的人建立了一个完整的基准: https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/extra/swift/sort

作为参考,GNU ISO C++ 标准库使用 3 的中值枢轴(根据 stl_algo.h 标头)。

【讨论】:

  • 跟进:如果选择第一项作为枢轴是一个糟糕的选择,那么更好的实现是什么?维基百科提到中位数为 3,但有一些警告。在我看来,与第一项相比,中位数为 3 的最坏情况会更难。
【解决方案3】:

在 Swift 5 的演变中,IntroSort 算法在 'sort()' 方法中被修改版本的 TimSort(Tim Peters 于 2002 年首次为 Python 实现)取代: https://github.com/apple/swift/blob/master/stdlib/public/core/Sort.swift

【讨论】:

    猜你喜欢
    • 2011-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-17
    • 1970-01-01
    • 2011-11-02
    • 2013-08-28
    • 1970-01-01
    相关资源
    最近更新 更多