std::remove 和 std::remove_if 设计的稳定性是否失败?

2024-02-20

最近(从一条评论中)我了解到std::remove and std:remove_if是稳定的。我是否错误地认为这是一个糟糕的设计选择,因为它阻止了某些优化?

想象一下删除 1M 的第一个和第五个元素std::vector。由于稳定性原因,我们无法实施remove与交换。相反,我们必须移动所有剩余的元素。 :(

如果我们不受稳定性的限制,我们实际上可以(对于 RA 和 BD iter)有 2 个 iter,一个从前面,第二个从后面,然后使用交换来结束要删除的项目。我相信聪明的人也许可以做得更好。我的问题是一般性的,而不是我所说的具体优化。

EDIT:请注意,C++宣扬的是零开销原则,而且还有std::sort and std::stable_sort排序算法。

EDIT2:优化将类似于以下内容:

For remove_if:

  • bad_iter 从头开始​​查找谓词返回 true 的元素。
  • good_iter 从末尾查找谓词返回 false 的元素。

当双方都找到了预期的结果时,他们就会交换各自的元素。终止时间为good_iter <= bad_iter.

如果有帮助的话,请将其想象为快速排序算法中的一个迭代器,但我们不将它们与特殊元素进行比较,而是使用上面的谓词。

EDIT3:我尝试着寻找最坏的情况(最坏的情况remove_if- 注意谓词为真的可能性是多么的少),我得到了这个:

#include <vector>
#include <string>
#include <iostream>
#include <map>
#include <algorithm>
#include <cassert>
#include <chrono>
#include <memory>
using namespace std;
int main()
{  
    vector<string> vsp;
    int n;
    cin >> n;
    for (int i =0; i < n; ++i)
    {   string s = "123456";
        s.push_back('a' + (rand() %26));
        vsp.push_back(s);
    }
    auto vsp2 = vsp;
    auto remove_start = std::chrono::high_resolution_clock::now();
    auto it=remove_if(begin(vsp),end(vsp), [](const string& s){ return s < "123456b";});
    vsp.erase(it,vsp.end());
    cout << vsp.size() << endl;
    auto remove_end = std::chrono::high_resolution_clock::now();
    cout << "erase-remove: " << chrono::duration_cast<std::chrono::milliseconds>(remove_end-remove_start).count() << " milliseconds\n";

    auto partition_start = std::chrono::high_resolution_clock::now();
    auto it2=partition(begin(vsp2),end(vsp2), [](const string& s){ return s >= "123456b";});
    vsp2.erase(it2,vsp2.end());
    cout << vsp2.size() << endl;
    auto partition_end = std::chrono::high_resolution_clock::now();
    cout << "partition-remove: " << chrono::duration_cast<std::chrono::milliseconds>(partition_end-partition_start).count() << " milliseconds\n";
}



C:\STL\MinGW>g++ test_int.cpp -O2 && a.exe
12345678
11870995
erase-remove: 1426 milliseconds
11870995
partition-remove: 658 milliseconds

对于其他用途,分区会更快、相同或更慢。让我感到困惑。 :D


我假设你问的是一个假设的定义stable_remove成为什么remove目前是,并且remove然而,实施者认为最好以任何顺序给出正确的值。期望实施者能够在做与以下完全相同的事情的基础上进行改进stable_remove.

实际上,图书馆不能easily做这个优化。这取决于数据,但您不想在决定如何删除每个元素之前花费太长时间来计算将删除多少个元素。例如,您可以进行一次额外的传递来对它们进行计数,但在很多情况下,额外的传递效率很低。仅仅因为在某些情况下不稳定的删除比稳定的删除速度更快,并不一定意味着在两者之间进行选择的自适应算法是一个不错的选择。

我认为之间的区别remove and sort排序是known这是一个复杂的问题,有很多不同的解决方案、权衡和调整。所有“简单”排序算法都很慢一般。大多数标准算法都非常简单,并且remove是其中之一但是sort不是。我认为定义它没有多大意义stable_remove and remove作为单独的标准功能。

编辑:您使用我的调整进行的编辑(类似于std::partition但不需要将值保留在右侧)对我来说似乎相当合理。它需要一个双向迭代器,但标准中已有在不同迭代器类别上表现不同的算法的先例,例如std::distance。因此标准可以定义unstable_remove只有requires一个前向迭代器,但如果它得到一个 bidi 迭代器,你就可以做你的事情了。该标准可能不会列出算法,但它可能有这样的短语“如果迭代器是双向的,则最多min(k, n-k)移动到哪里k是删除的元素数量”,这实际上会强制它。但请注意,该标准目前并未说明移动次数remove_if确实如此,所以我认为确定这一点根本不是一个优先事项。

当然,没有什么可以阻止您实现自己的unstable_remove.

如果我们接受标准不需要指定不稳定的删除,那么问题就归结为是否应该调用它定义的函数stable_remove, 展望未来remove对于双向迭代器来说,它的行为有所不同,并且如果用于执行不稳定删除的一些聪明的启发式方法变得足够众所周知,值得作为标准函数,那么对于前向迭代器来说,它的行为也可能有所不同。我想说不是:如果标准函数的名称不完全规则,那并不是一场灾难。从 STL 中删除稳定性保证可能会造成相当大的破坏remove_if。那么问题就变成了,“为什么 STL 不把它称为stable_remove_if”,对此我只能回答,除了所有答案中提出的所有观点之外,STL设计过程比标准化过程要快得多。

stable_remove还会打开一个关于其他标准功能的蠕虫罐头,这些功能可能理论上有不稳定的版本。对于一个特别愚蠢的例子应该copy叫做stable_copy,以防万一存在某种实现,在复制时反转元素的顺序明显更快?应该copy叫做copy_forward,以便实现可以选择哪一个copy_backward and copy_forward被称为copy根据哪个更快?委员会的部分工作就是在某处划定界限。

我认为实际上当前的标准是明智的,单独定义一个stable_remove and a remove_with_some_other_constraints, but remove_in_some_unspecified_way只是没有提供同样的优化机会sort_in_some_unspecified_way做。 Introsort 是在 1997 年发明的,当时 C++ 正在标准化,但我不认为周围的研究工作remove与过去和周围的情况完全一样sort。我可能错了,正在优化remove可能是下一件大事,如果是这样,那么委员会就错过了一个机会。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

std::remove 和 std::remove_if 设计的稳定性是否失败? 的相关文章

  • Volatile.Read 和 Volatile.Write 背后的逻辑是什么?

    来自 MSDN Volatile Read 读取字段的值 在需要它的系统上 插入一个 阻止处理器重新排序内存的内存屏障 操作如下 如果在该方法之后出现读或写 代码 处理器无法移动它before这个方法 and Volatile Write
  • 在 C# 中使用“using”关键字避免多次处置的最佳实践

    当变量是 IDisposable 时 我们有using关键字来管理处置 但是如果我们在方法中返回值怎么办 using twice StringContent stringToStringContent string str using St
  • C free() 是如何工作的? [复制]

    这个问题在这里已经有答案了 可能的重复 malloc 和 free 如何工作 https stackoverflow com questions 1119134 how malloc and free work include
  • 并行化斐波那契序列生成器

    我正在学习并行化 在一项练习中 我得到了一些我应该提高性能的算法 其中之一是斐波那契数列生成器 array 0 0 array 1 1 for q 2 q lt MAX q array q array q 1 array q 2 我怀疑 这
  • 如果 JSON.NET 中的值为 null 或空格,则防止序列化

    我有一个对象需要以这样的方式序列化 即 null 和 空白 空或只是空格 值都不会序列化 我不控制对象本身 因此无法设置属性 但我知道所有属性都是字符串 环境NullValueHandling显然 忽略 只能让我找到解决方案的一部分 它 似
  • 将 OpenCV Mat 转换为数组(可能是 NSArray)

    我的 C C 技能很生疏 OpenCV 的文档也相当晦涩难懂 有没有办法获得cv Mat data属性转换为数组 NSArray 我想将其序列化为 JSON 我知道我可以使用 FileStorage 实用程序转换为 YAML XML 但这不
  • C# 处理标准输入

    我目前正在尝试通过命令行断开与网络文件夹的连接 并使用以下代码 System Diagnostics Process process2 new System Diagnostics Process System Diagnostics Pr
  • 如何以编程方式播放 16 位 pcm 数组 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我有一个包含 16 位 pcm 值的短 数组 我希望能够在不添加任何标题 也不将任何文件保存到内存的情况下播放它 我知道我可能需要一个提供
  • 将下拉列表与字典绑定

    我将字典绑定到下拉列表 举例来说 我的字典中有以下项目 Test1 123 Test2 321 我希望下拉文本采用以下格式 Test1 Count 123 Test2 Count 321 我沿着以下路径走 但没有运气 MyDropDown
  • 带有运算符语法的错误消息,但不带有函数语法的错误消息

    为什么我在调用 unary 时收到错误消息 使用运算符语法 如果我用函数语法调用它就可以了 现场演示 https godbolt org z j7AbeQ template
  • C# 编译器数字文字

    有谁知道 C 编译器数字文字修饰符的完整列表 默认情况下 声明 0 使其成为 Int32 声明 0 0 使其成为 Double 我可以在末尾使用文字修饰符 f 来确保某些内容被视为 Single 例如像这样 var x 0 x is Int
  • 在 C++11 中移出 stdpriority_queue 的元素

    最小的工作示例 include
  • 用于连接 DataTable 上的动态列的动态 LINQ

    我目前遇到的情况不确定如何继续 我有两个从数据库填充的数据表 我还有一个可用的列名称列表 可用于将这两个数据表连接在一起 我希望编写一组 LINQ 查询 这些查询将 显示两个数据表中的行 内部联接 用于从一个数据表更新另一个数据表 显示一个
  • 为什么 f(i = -1, i = -1) 是未定义的行为?

    我正在读关于违反评估顺序 http en cppreference com w cpp language eval order 他们举了一个令我困惑的例子 1 如果标量对象上的副作用相对于同一标量对象上的另一个副作用是无序的 则行为未定义
  • 浮点字节序?

    我正在为实时海上模拟器编写客户端和服务器 并且由于我必须通过套接字发送大量数据 因此我使用二进制数据来最大化可以发送的数据量 我已经了解整数字节顺序以及如何使用htonl and ntohl为了规避字节顺序问题 但我的应用程序与几乎所有模拟
  • 在哪里可以下载没有 Visual Studio 2010 的 C# 4.0 编译器?

    我知道 CTP VS 2010 映像 但我可以只下载 NET Framework 4 0 和 C 编译器吗 AFAIK VS 2010 CTP 仅作为 VM 映像提供 我不相信 Microsoft 发布了 VS 的安装程序 其中一个绝对不适
  • C 语言中的 Alpha 混合 2 RGBA 颜色[重复]

    这个问题在这里已经有答案了 可能的重复 如何快速进行阿尔法混合 https stackoverflow com questions 1102692 how to do alpha blend fast 对 2 个 RGBA 整数 颜色进行
  • printf或iostream如何指定点后的最大位数

    字符串采用什么格式printf or iomanip我应该使用 iostream 中的运算符以以下格式打印浮点数 125 0 gt 125 125 1 gt 125 1 125 12312 gt 125 12 1 12345 gt 1 12
  • 如果“嵌入式”SQL 2008 数据库文件不存在,如何创建它?

    我使用 C ADO Net 和在 Server Management Studio 中创建的嵌入式 MS SQL 2008 数据库文件 附加到 MS SQL 2008 Express 创建了一个数据库应用程序 有人可以向我指出一个资源 该资
  • 为什么表达式 a = a + b - ( b = a ) 在 C++ 中给出序列点警告?

    以下是测试代码 int main int a 3 int b 4 a a b b a cout lt lt a lt lt a lt lt lt lt b lt lt b lt lt n return 0 编译此命令会出现以下警告 gt g

随机推荐