当前位置: 首页 > news >正文

JavaScript洗牌算法实践

在 JavaScript 中,最常用且公平的洗牌算法是 Fisher-Yates 洗牌算法(也称 Knuth 洗牌算法),核心逻辑是从数组末尾向前遍历,每次将当前元素与前面随机位置的元素交换,确保每个元素被打乱的概率均等。

1. 基础实现(原地洗牌)
直接修改原数组,空间复杂度为 O(1),适合对性能要求较高的场景。

javascript:/*** Fisher-Yates 原地洗牌算法* @param {Array} arr - 待洗牌的数组* @returns {Array} 洗牌后的数组(与原数组引用相同)*/
function shuffleArray(arr) {// 从数组最后一位开始向前遍历for (let i = arr.length - 1; i > 0; i--) {// 生成 [0, i] 范围内的随机整数(确保每个位置概率均等)const randomIndex = Math.floor(Math.random() * (i + 1));// 交换当前元素与随机位置元素[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];}return arr;
}// 测试
const arrs= ["猫", "狗", "兔", "仓鼠", "鹦鹉"];
shuffleArray(arrs);
console.log(arrs); // 示例输出:["兔", "猫", "鹦鹉", "狗", "仓鼠"](每次结果不同)

2. 非原地洗牌(不修改原数组)

通过创建原数组的副本进行洗牌,避免改变原数组,适合需要保留原始数据的场景。

javascript:/*** Fisher-Yates 非原地洗牌算法* @param {Array} arr - 待洗牌的数组* @returns {Array} 新的洗牌后数组(原数组不变)*/
function shuffleArrayWithoutMutate(arr) {// 创建原数组的浅拷贝(若数组元素是对象,需用深拷贝,如 JSON.parse(JSON.stringify(arr)))const newArr = [...arr];for (let i = newArr.length - 1; i > 0; i--) {const randomIndex = Math.floor(Math.random() * (i + 1));[newArr[i], newArr[randomIndex]] = [newArr[randomIndex], newArr[i]];}return newArr;
}// 测试
const numbers = [1, 2, 3, 4, 5];
const shuffledNumbers = shuffleArrayWithoutMutate(numbers);
console.log(numbers); // 输出:[1, 2, 3, 4, 5](原数组不变)
console.log(shuffledNumbers); // 示例输出:[3, 5, 1, 4, 2]

3. 常见错误:“随机交换”的陷阱

新手常犯的错误是用「遍历数组,每次与随机位置交换」的方式洗牌,这种方式会导致元素概率不均(靠前元素被交换的概率更高),不推荐:

javascript:// 错误示例:概率不均的洗牌
function badShuffle(arr) {for (let i = 0; i < arr.length; i++) {// 随机位置范围是 [0, arr.length-1],而非 [0, i],导致概率偏差const randomIndex = Math.floor(Math.random() * arr.length);[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];}return arr;
}

核心原理

Fisher-Yates 算法的公平性源于:

- 第 1 个元素(最终在末尾)有  1/n  的概率被选中;
- 第 2 个元素有  1/(n-1)  的概率被选中;
- 以此类推,每个元素最终在任意位置的概率均为  1/n ,确保绝对公平。

http://www.dtcms.com/a/394573.html

相关文章:

  • 掌握timedatectl命令:Ubuntu 系统时间管理指南
  • 【RT Thread】RTT内核对象机制详解
  • Seata分布式事务
  • 用例图讲解
  • makefile原理
  • AUTOSAR CP开发流程总结
  • 通过VNC实现树莓派远程桌面访问
  • linux信号done
  • BeanUtils.copyProperties 映射规则详解
  • 物联网 frid卡控制
  • LeetCode刷题记录----322.零钱兑换(Medium)
  • 2015/07 JLPT听力原文 问题四
  • Redis集群实验
  • 昇腾生态双支柱:MindSpore 与 CANN 的全栈技术解析
  • YOLO系列——实时屏幕检测
  • 牛客算法基础noob49 上三角矩阵判定
  • autosar 中OS模块理解
  • 通俗范畴论17.2 向量空间的对偶与双对偶
  • huggingface_hub 安装部署问题汇总
  • 在我的Java项目中为什么使用AllArgsConstructor注解注入的方式启动报错了:
  • π0:一个 VLA 流匹配模型用于通用机器人控制(又称 pi0)
  • Information theorem-Entropy
  • 编译原理实验报告——词法分析程序
  • 整体设计 完整的逻辑链条 之4 认知逻辑视角 —— 前序驱动的认知演进体系 之2
  • C/C++正则表达式PCRE2库
  • 基于python大数据的声乐信息分类评测系统
  • 永磁同步电机无速度算法--改进型超螺旋滑模观测器
  • Linux0.12的中断处理过程源码分析
  • 进程控制(Linux)
  • 【C++】——string类的使用(详细讲解)