了解两种矩阵旋转算法的性能



我对基础知识缺乏深入的了解,这导致了这些类型的问题解决挑战。

HackerRank矩阵旋转问题是一个非常有趣的问题。我建议那些试图丰富编码技能的人使用hackerrank(https://www.hackerrank.com/challenges/matrix-rotation-algo)

问题总结是,你得到一个 R x C 整数矩阵,其中 R 和 C 的最小值必须是偶数。您必须逆时针旋转矩阵 x 次。旋转适用于矩阵的元素,而不是矩阵维度,以防它不清楚。

所以我用两种算法解决了这个问题。它们都非常相似,因为您可以想象矩阵就像洋葱层一样,您可以在其中循环穿过每一层,并旋转该层中的元素。旋转次数只是 x %(该层中的元素计数),因此如果给定 x=1,000,000,则重复完整旋转是没有意义的。

第一个,也是最快的是:https://codetidy.com/8002/

第二个不是遍历旋转次数,而是做一些繁重的逻辑和数学来找出将元素移动到哪里。https://codetidy.com/8001/

所以当我写第二个的时候,我假设它会更快,因为你不会遍历每一层的最大旋转次数。然而,它最终进展得更慢。

我不太明白为什么。我在控制台中记录了迭代次数,第一个迭代次数增加了 50 倍,但速度更快。

迭代次数不是一切。以下是一些可能影响性能的一般事项。

数组和矩阵要记住的一件重要事情是缓存命中。如果您的操作生成大量缓存命中,它们看起来会快几个数量级。要获得缓存命中,通常需要按内存顺序排列。对于按顺序向前的数组。对于矩阵,这意味着首先递增最低索引。要获取未命中数,您需要以大于缓存行大小(取决于 CPU)的增量跳转。有趣的实验:基准for (i...) for (j...) ++m[i][j]for (i...) for (j..) ++m[j][i]看看差异。

在您的情况下,我猜更快的方法至少在水平部分具有非常线性的访问。

然后是分支预测。现代 CPU 通过管道传输指令,以更好地利用现有硬件。分支 (IF) 会中断管道,因为您不知道要采用哪条路径(该指令仍在执行)。作为优化,编译器/CPU 选择一个并开始处理,如果条件结果是另一种方式,它将丢弃所有内容并重新开始处理。检查通常给出相同结果的东西(如i<n)会比更难预测的东西更快。

这些是一些低层次的原因,为什么更简单的方法可能看起来更快。添加一些更高层次的原因(例如编译器未按预期方式优化代码),您会得到这样的结果。

重要说明:复杂性反映了渐近行为。是的,对于足够大的矩阵,第二种方法会更快,而且用于此问题的大小很可能不够大。

最新更新