std::shuffle是C++11起推荐的打乱方式,需传入UniformRandomBitGenerator引擎(如std::mt19937),不可用std::rand或已移除的std::random_shuffle;支持vector、array和原生数组,但要注意迭代器范围与类型安全。

std::shuffle 是 C++11 起推荐的打乱方式
直接用 std::shuffle,别再手写 Fisher-Yates 或调 std::random_shuffle(C++17 已移除)。它要求传入一个符合 UniformRandomBitGenerator 要求的随机数引擎,比如 std::mt19937,不能只传 std::rand。
常见错误是沿用旧教程写法:
std::random_shuffle(vec.begin(), vec.end()); // 错!C++17 删除,且线程不安全
- 必须显式提供随机引擎,否则编译失败(C++11 及以后)
-
std::mt19937比std::default_random_engine更可靠,尤其在多次调用时 - 种子建议用
std::random_device生成,避免重复序列:std::mt19937 g{std::random_device{}()};
打乱 std::array / 原生数组要小心迭代器类型
std::shuffle 接收前向迭代器,对 std::array 和 C 风格数组都可用,但写法稍有差异:
-
std::array✅a = {1,2,3,4,5}; std::shuffle(a.begin(), a.end(), g); - 原生数组需用指针:
int arr[5] = {1,2,3,4,5}; std::shuffle(arr, arr + 5, g);✅ - 错例:
std::shuffle(&arr[0], &arr[5], g)语法合法但易读性差,不推荐
注意:传入的尾迭代器必须是 end,不是 end - 1,否则会漏掉最后一个元素或越界。
立即学习“C++免费学习笔记(深入)”;
如果只能用 C++98 / 不支持 怎么办
没有 头、也不能用 std::shuffle 时,退回到手动实现 Fisher-Yates(Knuth shuffle),但必须用 std::rand() 配合 std::srand() 初始化——不过这有严重缺陷:
-
std::rand()周期短、低位随机性差,rand() % n会导致分布偏斜 - 多线程下
std::rand共享状态,不安全 - 若必须用,至少写成:
std::swap(arr[i], arr[rand() % (i + 1)]);(从后往前)
更稳妥的兼容方案是引入轻量 PRNG,例如 xorshift(几行代码),比依赖全局 rand 可控得多。
打乱 vector 或自定义类型要注意什么
std::shuffle 只要求元素可移动(或可复制),对 std::string、自定义 class 完全没问题——前提是它们满足 MoveConstructible 和 MoveAssignable。
- 若类禁用了移动操作(如显式删除了移动构造函数),会触发复制,性能下降但通常仍能编译通过
- 若元素含裸指针或手动管理资源,确保移动/复制语义正确,否则打乱后可能悬空
- 无须重载任何运算符;
std::shuffle不比较元素,只交换位置
真正容易被忽略的是:打乱前后,所有迭代器、引用、指针全部失效(对 std::vector 来说,这是 realloc 导致的)。如果打乱前保存了 &vec[3],打乱后它不一定还指向原值。










