计算使用 C++ std 函数而不是 for 循环的大 O
Posted
技术标签:
【中文标题】计算使用 C++ std 函数而不是 for 循环的大 O【英文标题】:calculating the Big O of using C++ std function instead of for loop 【发布时间】:2019-08-27 20:43:18 【问题描述】:如果我正在实现一个 for 循环来在数组中的两个索引之间向右移动元素,那么我需要 O(n) 来移动这些元素,但是如果我使用像 std::rotate
这样的库函数会怎样?一步执行,这是否意味着它需要恒定的时间。
喜欢这个例子
vector<int> arr = 1,2,3,4,5,6,7,8,9,10;
int temp = arr[5];
for(int i = 5 ; i >= 3 ; i--)
arr[i]=arr[i-1];
arr[2] = temp;
所以arr
将是 1,2,6,3,4,5,7,8,9,10
我可以这样做
rotate(arr.begin()+2,arr.begin()+5,arr.begin()+6);
...这将导致相同的移位数组
但第一个大约需要 3 步,而另一个只有 1 步,是这样吗?
【问题讨论】:
信息found here 好吧,它可能是一行代码,但它仍然需要进行 n 次迭代才能旋转它,例如你可以用你的代码创建一个函数并调用它(它是一行代码,但它不会成功一个常数复杂度函数) 请注意,如果您将“n”定义为 arr 的长度,for(int i = 5 ; i >= 3 ; i--)
和 rotate(arr.begin()+2,arr.begin()+5,arr.begin()+6);
都是 O(1)。
以源代码行数衡量的“步骤数”根本没有意义。
@FrançoisAndrieux 是的,这是肯定的,我只是想知道 C++ 编译器是否在调用 rotate
函数时发挥了他的一些黑魔法
【参考方案1】:
std::rotate
具有线性复杂性,因此虽然它可能比您的代码稍快或稍慢,但两者使用的步骤数大致相同。
在这种特殊情况下,std::rotate
很可能实际上使用了更多步骤,因为它被编码为处理任意数量的位置的旋转,这比像您的代码那样总是只旋转一个位置稍微多一点工作。另一方面,旋转一个位置也可能很常见,标准库可以检查这一点,并针对特定情况使用与您类似的代码。
【讨论】:
如果std::rotate
的参数是常量表达式,假设编译器将内联并可能展开循环(如果它是有益的)似乎是合理的。
@FrançoisAndrieux:在这种情况下,问题在于算法,而不是循环展开之类的问题。 std::rotate
的通常实现使用 2N 次交换,其中 N 是旋转的项目数。
我想不清楚我是专门回复第二部分的。如果可以确定步数是一个常数表达式(例如在这种情况下),那么支持 N 大小步的额外开销可能会消失。听起来您将我的评论解释为两种解决方案都是O(N)
的争议。我并不是说优化会改变这一点。
@FrançoisAndrieux:不,我没有争论任何事情,只是指出除了通常的展开等之外,还需要一些额外的工作。相反,如果您旋转的元素数量是恒定的并且可能低于某个阈值,则您实际上必须切换到完全不同的算法。所以从理论上讲,你可以做到这一点是对的——但我还没有看到可以做到这一点的实现,老实说也不希望很快做到。【参考方案2】:
不,它确实需要线性时间。实际时间复杂度是初始节点和最终节点之间的差异。查看 https://en.cppreference.com/w/cpp/algorithm/rotate
【讨论】:
以上是关于计算使用 C++ std 函数而不是 for 循环的大 O的主要内容,如果未能解决你的问题,请参考以下文章
为啥 C 和 C++ for 循环使用 int 而不是 unsigned int?
如何使用 OpenMP 通过 C++ std::list 并行化 for 循环?