c++中如何打乱数组顺序_c++ shuffle函数随机排列方法【汇总】

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::mt19937std::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++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],打乱后它不一定还指向原值。