[英]stable_partition on forward iterators
1.在当前的标准草案中, std::stable_partition
的规范是:
template<class BidirectionalIterator, class Predicate> BidirectionalIterator stable_partition( BidirectionalIterator first, BidirectionalIterator last, Predicate pred);
我没有发现 (见下文)BidirectionalIterator
应该是一个双向迭代器的要求,但这个名字表明是这样。
2.在SGI STL中,规范是:
template <class ForwardIterator, class Predicate> ForwardIterator stable_partition( ForwardIterator first, ForwardIterator last, Predicate pred);
对类型的要求:
ForwardIterator
是 Forward Iterator 的模型。
当前标准和 SGI 版本的复杂性规范是相同的:最多N log N
交换,如果有足够的额外内存和谓词和投影的准确N
应用,则只有O(N)
交换。
template<typename ForwardIterator, typename Predicate>
ForwardIterator stable_partition(
ForwardIterator first, ForwardIterator last, Predicate pred);
在 GCC 和 Clang 中, std::stable_partition
确实适用于前向迭代器。 例如:
int main() {
std::forward_list<int> list{1, 4, 5, 2, 3, 0};
std::stable_partition(list.begin(), list.end(), [](int i) { return i < 3;});
for (auto v : list)
std::cout << v << ' ';
}
编译并产生正确的输出。 Microsoft 的编译器无法编译此代码(没有--
运算符)。 英特尔的一个成功了。
我有两个相关的问题:
std::stable_partition
是否至少接受标准的双向迭代器,或者名称BidirectionalIterator
是否具有误导性?编辑。
发现这个条款:
如果算法的模板参数名为
BidirectionalIterator
、BidirectionalIterator1
或BidirectionalIterator2
,则模板参数应满足Cpp17BidirectionalIterator
要求。
所以,只剩下第二个问题了。
首先,没有放弃支持, std::stable_partition
一直需要BidirectionalIterator
标准。 这并不意味着库的实现者不允许对输入参数提供较少的限制(如果它继续遵守标准 ofc 的其他部分)。 因此 Gcc、Clang 和 Intel 使用他们的权利并使代码更加通用。 您可以将其视为标准库的编译器扩展。
话虽如此,可能有人会问为什么标准在这里需要BidirectionalIterator
。 我想这是可能的,因为标准的作者没有看到没有这个要求就符合复杂性要求的方法。 gcc 的作者可能找到了一种比标准预期的更好的方法。 查看 gcc 源代码有点证实了这一点。 https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/stl_algo.h#L1613
编辑:
我已经深入研究了 GCC 库的实现,我想我明白了。 ForwardIterator
、 BidirectionalIterator
和RandomAccessIterator
实现对于std::stable_partition
是不同的。 这是由于分区算法使用的std::rotate
不同实现。 因此,对于前向迭代器,交换次数更大并且可以超过(last - first) * log(last - first)
。 看这里和这里。
这个问题似乎有历史原因而不是数学原因。 翻阅 Alexander Stepanov 的论文,我发现了这篇:“分区和相关函数”。
它包含以下段落:
备注:有趣的是,这种优秀的算法不在需要双向迭代器进行分区的 C++ 标准中。 我知道、实施和教授这个算法已经有一段时间了——因为我第一次在 80 年代中期在 CACM 的 Bentley 专栏中读到了它。 但是我最初的 STL 提议以某种方式为
partition
和stable_partition
指定了双向迭代器。 它们都在 SGI STL 中得到了纠正,但大多数供应商仍然落后。 这件小事已经困扰我 10 多年了; 最麻烦的部分是遗漏的事实。 它怎么发生的? 我怀疑这个解释很简单:虽然在 90 年代初我已经理解了将每种算法减少到最低要求的想法,而且我也知道当我们对数据了解得更多时,可以使用更好的算法来实现相同的操作。如果有这样的算法,我还没有完全意识到需要为最弱的情况提供算法。 又花了几年时间才明白“填充算法空间”的重要性。
下面的简单算法
template <typename I, // I models Forward Iterator
typename N, // N models Integer
typename P> // P models Unary Predicate
pair<I, I> stable_partition_inplace_n(I f, N n, P p)
{
if (n == 0) return make_pair(f, f);
if (n == 1) {
I l = successor(f);
if (p(*f)) l = f;
return make_pair(f, l);
}
pair<I, I> i = stable_partition_inplace_n(f, n/2, p);
pair<I, I> j = stable_partition_inplace_n(i.second, n – n/2, p);
return make_pair(rotate(i.first, i.second, j.first), j.second);
}
在那篇论文中给出。 它与前向迭代器一起工作并在最坏的情况下执行O(N log N)
交换。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.