欢迎光临
我们一直在努力

JS小知识点之打乱数组的方法(洗牌)

洗牌

  • 前言
  • 一、完全随机版(sort(() => Math.random() – 0.5))
    • 1.实现代码
    • 2.核心原理
    • 3. 优缺点
    • 4. 适用场景
  • 二、严谨版打乱(Fisher-Yates 洗牌算法)
    • 1. 实现代码
    • 2. 核心原理
    • 3. 优缺点分析
    • 4. 适用场景
  • 三、权重版打乱(Map 随机映射)
    • 1. 实现代码
    • 2. 核心原理
    • 3. 适用场景
  • 总结

前言

在 JavaScript 开发中,数组打乱(又称「洗牌」)是一个高频需求,比如游戏中的随机道具排列、抽奖活动的奖品顺序、展示列表的随机刷新等。今天我们就来拆解数组打乱的 3 种常用方法,从简洁易用到严谨高效,帮你根据场景选择最合适的方案。


提示:以下是本篇文章正文内容,下面案例可供参考

一、完全随机版(sort(() => Math.random() – 0.5))

这是最简洁的数组打乱方法,一行代码即可实现,也是前端新手最容易上手的方案。

1.实现代码

const animalList = ['🐶', '🐱', '🐭', '🐹', '🐰'];
// 数组打乱核心代码
const shuffledArr = animalList.sort(() => Math.random() 0.5);
console.log(shuffledArr); // 每次输出结果不同,如 ['🐱', '🐶', '🐰', '🐭', '🐹']

2.核心原理

  • sort() 方法的 sort() 是数组的排序方法,它接受一个比较函数作为参数,排序结果完全由这个比较函数的返回值决定: 返回 大于 0 的数:交换两个比较元素的位置; 返回 小于 0 的数:保持两个比较元素的原有位置; 返回 等于 0:不改变两个元素的相对位置。 这里的关键是:我们不需要基于元素本身的值排序,只需要通过随机返回正数 / 负数,让 sort() 随机调整元素位置。

  • Math.random() – 0.5 的随机开关 Math.random() 会返回一个 [0, 1) 区间内的随机小数; 用这个随机小数减去 0.5,会得到一个 (-0.5, 0.5) 区间内的随机数; 结果为负数的概率约 50%(保持原位),结果为正数的概率约 50%(交换位置)。

  • 两者结合,sort() 会按照随机决定两个元素是否交换,经过反复调整数组元素位置,最终实现数组完全随机的打乱。

    3. 优缺点

  • 优点 代码极简,一行实现,无需额外封装函数; 上手成本低,适合快速测试、短数组打乱等简单场景; 无额外依赖,兼容所有现代浏览器。
  • 缺点 不完全公平:由于 sort() 方法的底层排序算法(如 Chrome 采用快速排序),部分元素被打乱到任意位置的概率不均等,可能出现打乱不彻底的情况; 性能一般:时间复杂度为 O(n log n),长数组场景下效率偏低。
  • 4. 适用场景

    适合普通业务场景,如短数组打乱、游戏中的随机打乱、非核心业务的列表随机展示等,无需追求绝对公平的场景。


    二、严谨版打乱(Fisher-Yates 洗牌算法)

    如果你的场景对公平性要求较高(如抽奖、棋牌游戏、金融相关的随机排序),那么 Fisher-Yates 洗牌算法(又称 Knuth 洗牌算法)是你的黄金选择,它能保证每个元素被打乱到任意位置的概率完全均等,且性能更优。

    1. 实现代码

    /**
    * Fisher-Yates 洗牌算法(纯函数,不修改原数组)
    * @param {Array} arr – 待打乱的数组
    * @returns {Array} – 打乱后的新数组
    */

    const fisherYatesShuffle = (arr) => {
    // 1. 复制原数组,避免修改原始数据(保证数据纯净)
    const copyArr = [arr];
    // 2. 从数组最后一个元素开始,向前遍历
    for (let i = copyArr.length 1; i > 0; i) {
    // 3. 生成 0 到 i 之间的随机索引(包含 0 和 i)
    const randomIndex = Math.floor(Math.random() * (i + 1));
    // 4. 交换当前元素(i)和随机索引元素(randomIndex)
    [copyArr[i], copyArr[randomIndex]] = [copyArr[randomIndex], copyArr[i]];
    }
    // 5. 返回打乱后的数组
    return copyArr;
    };

    // 测试使用
    const animalList = ['🐶', '🐱', '🐭', '🐹', '🐰'];
    const shuffledArr = fisherYatesShuffle(animalList);
    console.log(shuffledArr); // 完全随机的结果,且每个元素概率均等

    2. 核心原理

    Fisher-Yates 洗牌算法的核心思想是从后往前,随机交换,步骤拆解如下:

  • 先复制原数组,避免修改原始数据
  • 从数组的最后一个元素开始向前遍历,不处理第一个元素(索引 0),因为遍历到最后时,它的位置自然确定
  • 对于当前遍历到的元素(索引 i),生成一个 [0, i] 区间内的随机索引 randomIndex,确保随机范围始终包含未被处理的元素
  • 交换当前元素(copyArr[i])和随机索引对应的元素(copyArr[randomIndex]),让当前元素随机落入未被处理的位置
  • 遍历完成后,所有元素都已被随机交换,得到完全打乱的数组。
  • 3. 优缺点分析

  • 优点 完全公平:每个元素被交换到任意位置的概率均等,无打乱不彻底的问题,适合高要求场景; 性能最优:时间复杂度为 O(n),仅遍历数组一次,长数组场景下效率远高于简洁版方法; 数据安全:纯函数设计,不修改原数组,避免污染原始数据。
  • 缺点 代码稍长,需要封装函数,比简洁版多一点上手成本
  • 4. 适用场景

    适合对公平性和性能有要求的场景,如抽奖活动、棋牌游戏、长数组打乱、金融 / 电商等核心业务的随机排序。


    三、权重版打乱(Map 随机映射)

    如果想在简洁性和公平性之间找一个平衡,还可以选择权重版打乱,它通过给每个元素绑定固定随机权重实现排序,比简洁版更公平,代码也相对精炼。

    1. 实现代码

    /**
    * 权重版数组打乱
    * @param {Array} arr – 待打乱的数组
    * @returns {Array} – 打乱后的新数组
    */

    const mapShuffle = (arr) => {
    // 1. 给每个元素绑定随机权重,生成 [{ value: 元素, weight: 随机数 }] 格式数组
    const mappedArr = arr.map(item => ({
    value: item,
    weight: Math.random()
    }));
    // 2. 按随机权重进行排序
    mappedArr.sort((a, b) => a.weight b.weight);
    // 3. 提取原元素,返回打乱后的数组
    return mappedArr.map(item => item.value);
    };

    // 测试使用
    const animalList = ['🐶', '🐱', '🐭', '🐹', '🐰'];
    const shuffledArr = mapShuffle(animalList);
    console.log(shuffledArr); // 公平性优于简洁版,代码优于 Fisher-Yates 算法

    2. 核心原理

  • 先通过 map() 方法遍历原数组,给每个元素绑定一个唯一的随机权重(Math.random() 生成);
  • 按照权重对数组进行排序,权重小的元素排在前面,权重大的元素排在后面;
  • 再通过 map() 方法提取绑定对象中的原元素,得到打乱后的数组。
  • 这种方法的公平性优于简洁版,因为每个元素的权重是固定的,排序过程中不会出现「重复随机」导致的概率不均。

    3. 适用场景

    适合普通业务场景中,既想追求一定公平性,又不想写复杂的 Fisher-Yates 算法的场景,如展示列表的随机刷新、简单游戏的道具排列等。

    总结

    以上就是今天要讲的内容,本文仅仅简单介绍了三种洗牌的方法。

    tips

  • 日常开发中,简单场景优先用 sort(() => Math.random() – 0.5),高效快捷;
  • 核心业务(如抽奖)优先用 Fisher-Yates 洗牌算法,保证公平与性能;
  • 无论使用哪种方法,都建议复制原数组后再操作,避免污染原始数据;
  • 数组打乱的核心是「随机调整元素位置」,不同方法的差异在于「随机的公平性」和「执行效率」。
  • 赞(0)
    未经允许不得转载:171主机测评 » JS小知识点之打乱数组的方法(洗牌)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址