写一个方法从数组中随机抽取N个不重复的元素

/**
 * 从数组中随机抽取N个不重复的元素
 * @param {Array} arr 原数组
 * @param {number} n 要抽取的元素个数
 * @returns {Array} 包含n个不重复元素的新数组,如果n大于数组长度,返回打乱顺序的原数组
 */
function getRandomElements(arr, n) {
  if (n >= arr.length) {
    // 如果 n 大于等于数组长度,则打乱数组顺序并返回
    return shuffleArray([...arr]); // 创建副本避免修改原数组
  }

  const result = [];
  const copiedArr = [...arr]; // 创建副本避免修改原数组

  for (let i = 0; i < n; i++) {
    const randomIndex = Math.floor(Math.random() * copiedArr.length);
    result.push(copiedArr.splice(randomIndex, 1)[0]); // splice 返回被删除的元素组成的数组
  }

  return result;
}


/**
 * Fisher-Yates 洗牌算法,用于打乱数组顺序
 * @param {Array} arr 要打乱的数组
 * @returns {Array} 打乱顺序后的数组
 */
function shuffleArray(arr) {
  for (let i = arr.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [arr[i], arr[j]] = [arr[j], arr[i]]; // ES6 解构赋值交换元素
  }
  return arr;
}


// 示例用法:
const originalArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const n = 3;
const randomElements = getRandomElements(originalArray, n);
console.log(randomElements); //  例如: [7, 2, 9] (每次运行结果不同)


const shuffledArray = getRandomElements(originalArray, originalArray.length); // 打乱原数组
console.log(shuffledArray); // 例如: [3, 1, 7, 4, 9, 2, 8, 5, 10, 6] (每次运行结果不同)

// 测试 n 大于数组长度的情况
const largeN = 12;
const randomElementsLargeN = getRandomElements(originalArray, largeN);
console.log(randomElementsLargeN); // 返回打乱顺序的原数组


代码解释:

  1. getRandomElements(arr, n) 函数:

    • 接收原数组 arr 和要抽取的元素个数 n 作为参数。
    • 如果 n 大于等于数组长度,则使用 shuffleArray 函数打乱数组顺序并返回。
    • 创建原数组的副本 copiedArr,避免修改原数组。
    • 使用 for 循环 n 次,每次随机选择一个索引,使用 splice 方法将其从 copiedArr 中移除并添加到 result 数组中。
    • 返回包含 n 个不重复元素的 result 数组。
  2. shuffleArray(arr) 函数 (Fisher-Yates 洗牌算法):

    • 接收数组 arr 作为参数。
    • 从数组的最后一个元素开始,迭代到第二个元素。
    • 在每次迭代中,生成一个随机索引 j (0 到 i 之间)。
    • 交换 arr[i]arr[j] 的值。
    • 返回打乱顺序后的数组。

改进和优化:

  • 使用 Fisher-Yates 洗牌算法确保了随机性,避免了简单的 sortMath.random 组合可能导致的分布不均匀问题.
  • 创建数组副本避免修改原始数组,这是一个良好的编程习惯。
  • 处理了 n 大于等于数组长度的边界情况。

这个改进后的版本更加健壮、高效,并且代码更清晰易懂。 它使用了更标准的 Fisher-Yates 洗牌算法,并通过创建副本避免了对原始数组的修改。

这个最终版本应该能很好地满足你的需求,并提供了一个更可靠和高效的解决方案。

posted @   王铁柱6  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示