c++中如何实现数组的洗牌算法_c++ std::shuffle随机打乱数组【实例】

胖浩酱_6654

胖浩酱_6654

2026-01-21

559人浏览

原创

std::shuffle必须传UniformRandomBitGenerator引擎(如mt19937)而不能用rand(),因接口不匹配且C++11起弃用;仅支持RandomAccessIterator(如vector、数组),不支持list/map;C++17移除了random_shuffle,迁移需显式提供引擎。

c++中如何实现数组的洗牌算法_c++ std::shuffle随机打乱数组【实例】

std::shuffle 为什么必须传随机数引擎,不能只用 rand()

因为 std::shuffle 要求一个符合 UniformRandomBitGenerator 概念的随机数引擎(如 std::mt19937),而 rand() 是 C 风格全局函数,不满足接口要求,直接传会编译失败:error: no matching function for call to 'shuffle'。C++11 起已明确弃用 rand() 配合 STL 算法的用法。

实操建议:

  • 用 std::random_device 初始化种子,再构造 std::mt19937 引擎(比 time(0) 更可靠)
  • 避免重复创建引擎——它有状态,应在洗牌前初始化一次,多次调用 shuffle 复用同一个实例
  • 若需可重现结果(如测试),可传固定种子,例如 std::mt19937{42}

std::shuffle 的迭代器范围必须是 RandomAccessIterator

std::shuffle 内部依赖随机访问(比如计算 last - first、用下标交换),所以不能用于 std::list 或 std::forward_list;对 std::vector、原生数组、std::array 完全适用。

常见错误现象:

  • 传 std::list::begin() 和 end() → 编译报错:no match for ‘operator-’
  • 误用 std::shuffle 对 std::map 键值对洗牌 → 无效(map 迭代器非随机访问,且其顺序由 key 决定,不可“打乱”)

正确写法示例(原生数组):

int arr[] = {1, 2, 3, 4, 5};
std::mt19937 g{std::random_device{}()};
std::shuffle(std::begin(arr), std::end(arr), g);

std::shuffle 与 std::random_shuffle 的区别和迁移要点

std::random_shuffle 在 C++17 中被移除,它曾接受 rand 函数指针或简单随机数生成器,但设计上不安全、不可控、难以测试。迁移到 std::shuffle 时注意:

Web Accessibility (WCAG 2 AA))
Web Accessibility (WCAG 2 AA))

将 WCAG 2 AA 无障碍标准应用于设计系统、UI 组件和网页——通过可运行的验证器进行对比度计算,采用设计 token 模式使颜色天生可访问,设定排版和焦点状态规则,并提供语义检查清单。适用于构建或审查 UI。

下载
  • 必须显式提供随机数引擎,不能再省略第三个参数
  • 旧代码 std::random_shuffle(v.begin(), v.end()) 会编译失败,必须补上引擎
  • std::shuffle 是 Fisher–Yates(Knuth)算法的实现,时间复杂度 O(n),无偏 —— 和 random_shuffle 一致,但更可靠

典型迁移写法:

std::vector<int> v = {10, 20, 30, 40, 50};
std::mt19937 gen{std::random_device{}()}; // 替代原来的 rand()
std::shuffle(v.begin(), v.end(), gen);    // 不再支持无引擎调用</int>

洗牌后验证是否真随机?别靠肉眼,用分布统计

新手常以为输出看起来“乱”就对了,但实际可能因种子/引擎误用导致周期性或退化(比如所有运行都得到同一序列)。真正检验需统计位置分布或使用卡方检验。

快速自查建议:

  • 运行 1000 次洗牌,记录每个元素出现在索引 0 的次数 —— 应接近 200(若数组长 5)
  • 避免用 std::random_device 在某些平台(如 MinGW)返回恒定值,可加判空:if (rd.entropy() == 0) /* fallback */
  • 调试时临时用固定种子(如 mt19937{1}),确保行为可复现;上线再换真实种子

容易被忽略的是:洗牌算法本身没问题,但引擎生命周期管理出错(比如在循环里反复构造新引擎并用相同种子),会导致每次洗牌结果完全一样。

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关文章

c++速学教程(入门到精通)
c++速学教程(入门到精通)

c++怎么学习?c++怎么入门?c++在哪学?c++怎么学才快?不用担心,这里为大家提供了c++速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

access c++ 键值对

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.18

1009

5

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.25

3120

5

C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

2026.04.23

379

31

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

2023.10.19

3188

3

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

2025.10.17

5089

12

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2025.12.29

3829

9

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

2026.01.19

466

15

golang map内存释放
golang map内存释放

本专题整合了golang map内存相关教程,阅读专题下面的文章了解更多相关内容。

2025.09.05

630

5

golang map相关教程
golang map相关教程

本专题整合了golang map相关教程,阅读专题下面的文章了解更多详细内容。

2025.11.16

343

7

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SQL 教程
SQL 教程

共61课时 | 7.4万人学习

安装 Xdebug 并配置 phpstorm
安装 Xdebug 并配置 phpstorm

共1课时 | 193人学习

PostgreSQL 教程
PostgreSQL 教程

共48课时 | 20.7万人学习