缓存优化 - Hashmap 与 QuickSort?
Cache Optimization - Hashmap vs QuickSort?
假设我有 N 个未排序的整数数组。我想找到这些数组的交集。
有两种解决此问题的好方法。
第一,我可以使用 nlogn 排序对数组进行就地排序,例如 QuickSort 或 MergeSort。然后我可以在每个数组的开头放置一个指针。将每个数组与其下方的数组进行比较,迭代较小的数组 [pointer] 的指针,或者如果它们都相等,则您找到了一个交集。
这是一个复杂度为 O(nlogn) 的解决方案,具有恒定的内存(因为一切都是就地完成的)。
第二种解决方案是使用散列映射,将出现在第一个数组中的值作为键,然后在遍历其余数组时递增这些值(然后获取值为N).这是一个 O(n) 的解决方案,具有 O(n) 内存,其中 n 是所有数组的总大小。
理论上前者的解是o(nlogn),后者是O(n)。然而,哈希映射没有很大的局部性,因为项目可以由于碰撞而随机散布在映射中。另一种解决方案虽然为 o(nlogn),但一次一个地遍历数组,表现出出色的局部性。由于 CPU 倾向于将当前索引旁边的内存中的数组值拉入缓存,因此 O(nlogn) 解决方案将比哈希映射解决方案更频繁地访问缓存。
因此,给定一个非常大的数组大小(随着元素数量趋于无穷大),o(nlogn) 解决方案实际上比 O(n) 解决方案更快是否可行?
对于整数,您可以使用非比较排序(请参阅 counting, radix sort). A large set might be encoded, e.g. sequential runs into ranges. That would compress the data set and allow for skipping past large blocks (see RoaringBitmaps)。有可能对硬件友好并且具有 O(n) 复杂性。
复杂性理论不考虑常数。正如您所怀疑的那样,由于隐藏的常量,具有更高复杂性的算法总是有可能比替代算法更快。通过利用问题的性质,例如将解决方案限制为整数,有一些潜在的优化不适用于通用方法。好的算法设计通常需要理解和利用这些约束。
假设我有 N 个未排序的整数数组。我想找到这些数组的交集。
有两种解决此问题的好方法。
第一,我可以使用 nlogn 排序对数组进行就地排序,例如 QuickSort 或 MergeSort。然后我可以在每个数组的开头放置一个指针。将每个数组与其下方的数组进行比较,迭代较小的数组 [pointer] 的指针,或者如果它们都相等,则您找到了一个交集。
这是一个复杂度为 O(nlogn) 的解决方案,具有恒定的内存(因为一切都是就地完成的)。
第二种解决方案是使用散列映射,将出现在第一个数组中的值作为键,然后在遍历其余数组时递增这些值(然后获取值为N).这是一个 O(n) 的解决方案,具有 O(n) 内存,其中 n 是所有数组的总大小。
理论上前者的解是o(nlogn),后者是O(n)。然而,哈希映射没有很大的局部性,因为项目可以由于碰撞而随机散布在映射中。另一种解决方案虽然为 o(nlogn),但一次一个地遍历数组,表现出出色的局部性。由于 CPU 倾向于将当前索引旁边的内存中的数组值拉入缓存,因此 O(nlogn) 解决方案将比哈希映射解决方案更频繁地访问缓存。
因此,给定一个非常大的数组大小(随着元素数量趋于无穷大),o(nlogn) 解决方案实际上比 O(n) 解决方案更快是否可行?
对于整数,您可以使用非比较排序(请参阅 counting, radix sort). A large set might be encoded, e.g. sequential runs into ranges. That would compress the data set and allow for skipping past large blocks (see RoaringBitmaps)。有可能对硬件友好并且具有 O(n) 复杂性。
复杂性理论不考虑常数。正如您所怀疑的那样,由于隐藏的常量,具有更高复杂性的算法总是有可能比替代算法更快。通过利用问题的性质,例如将解决方案限制为整数,有一些潜在的优化不适用于通用方法。好的算法设计通常需要理解和利用这些约束。