【发布时间】:2017-04-24 07:01:01
【问题描述】:
我一直在挑选和探索 Swift Array 的排序功能,我惊讶地发现对 500,000 个整数的数组进行就地排序比对 500,000 个元组的数组进行排序要快得多(快 5 倍)。我对这个发现感到惊讶,因为在询问了上一个关于 Stack Overflow 的问题并深入研究了 Swift 开源之后,似乎 Swift 只是简单地使用了 introSort() 来进行排序算法。我试图解析代码以查看它是否以任何不同/特殊的方式处理整数排序,但我找不到任何东西。时间复杂度明智的 IntroSort() 执行 NlogN avg 和最坏情况,因此我的元组排序和 int 排序应该表现相同。如果你算一下,5 倍的加速开始看起来像线性与 NlogN 时间复杂度。
这让我想到,也许 Swift IS 使用线性时间整数排序算法来处理整数排序,也许我只是错过了它。现在我不是整数排序方面的专家,但似乎整数排序算法最可行的候选者是鸽巢排序、countingSort 和 RadixSort。我马上就排除了基数排序,因为它的时间复杂度是 O(wn),其中 w 是 word/int 大小。这意味着通用排序中的 (logN) 项需要大于 w,这将需要我们对一个大得离谱的列表进行排序。查看 int 排序与元组排序的性能,我推断基数排序不可能在这里使用。
按淘汰顺序,这剩下 CountingSort 和 Pigeonhole Sort。它们都表现最坏情况 O(N + k),其中 N 是元素的数量,k 是值的范围。因此,为了确定 Swift 的算法是否属于这些算法之一,我认为将 500,000 个具有较小键值的整数的排序与 500,000 个具有大量键的整数的排序进行比较就足够了。对于第一个数组,我选择了 [1, 500,000] 的键范围。对于第二个数组,我选择了 [1, 6,000,500,000] 的键范围。如果第二个数组使用计数排序或鸽巢排序(只是做数学),你会期望第二个数组的排序比第一个数组花费更长的时间,但事实并非如此。令我惊讶的是,性能完全一样!下面是我的示例代码和性能结果:
func testTupleSortVsIntegerSort()
{
var intArray = [Int]()
for value in 1..<500000 {
intArray.append(value)
}
var tupleArray = intArray.map{($0, "sentinelValue")}
intArray.shuffle()
tupleArray.shuffle()
var startTime = CFAbsoluteTimeGetCurrent()
intArray.sort()
var endTime = CFAbsoluteTimeGetCurrent()
print("Integer sort took \(endTime - startTime) seconds")
startTime = CFAbsoluteTimeGetCurrent()
tupleArray.sort {
return $0.0 < $1.0
}
endTime = CFAbsoluteTimeGetCurrent()
print("Tuple sort took \(endTime - startTime) seconds")
}
func testIntegerSortWithSmallKeyRangeVsVeryLargeKeyRange()
{
var intArrayWithSmallKeys = [Int]()
for value in 1..<500000 {
intArrayWithSmallKeys.append(value)
}
var intArrayWithLargeKeys: [Int] = intArrayWithSmallKeys.map {
if $0 < 250000 {
return $0
} else if $0 < 350000 {
return $0 + 50000000
} else {
return $0 + 6000000000
}
}
intArrayWithSmallKeys.shuffle()
intArrayWithLargeKeys.shuffle()
var startTime = CFAbsoluteTimeGetCurrent()
intArrayWithSmallKeys.sort()
var endTime = CFAbsoluteTimeGetCurrent()
print("Integer sort with small keys took \(endTime - startTime) seconds")
startTime = CFAbsoluteTimeGetCurrent()
intArrayWithLargeKeys.sort()
endTime = CFAbsoluteTimeGetCurrent()
print("Integer sort with large keys took \(endTime - startTime) seconds")
}
Test Case '-[ColorExtractorTests.GeneralPerformanceTest testTupleSortVsIntegerSort]' started.
Integer sort took 0.659438014030457 seconds
Tuple sort took 3.40226799249649 seconds
Test Case '-[ColorExtractorTests.GeneralPerformanceTest testIntegerSortWithSmallKeyRangeVsVeryLargeKeyRange]' started.
Integer sort with small keys took 0.665884971618652 seconds
Integer sort with large keys took 0.669007003307343 seconds
这里发生了什么?是否有一些我不知道的神奇整数排序算法?我对这些排序算法的理解是错误的还是我的分析中有一些缺陷?
【问题讨论】:
-
Swift 使用 introsort,比较 Swift sorting algorithm implementation.
-
@MartinR 在我的问题中我说我已经看过了。我知道这似乎是 introSort,但我的发现另有说明。如果它使用的是 introSort,那么我发布的场景的性能应该是相同的,但事实并非如此。这就是为什么我想知道 Swift 是如何对 INT 进行特别排序的,以及是否有人知道它是否以不同的方式处理它们。我解析了代码,并没有看到 Swift 将整数排序作为一种特殊情况处理,所以我很困惑。
-
请注意 - 您可以有两种算法,两种算法都具有
O(n*log n)时间复杂度,但在 500,000 个元素上仍然可以比另一种快 5 倍。 -
我没有正确阅读您的问题,我深表歉意。 – 我认为没有单独的整数算法。但是例如比较和交换元组可能会更慢。您的元组还包含一个字符串,以便完成引用计数。您可以尝试使用 Instruments 进行分析,以查看时间都花在了哪里。
-
另外使用自定义闭包排序似乎要慢得多,试试
intArray.sort { $0 < $1 }。
标签: arrays swift algorithm sorting integer