/**
* 从数组中随机抽取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); // 返回打乱顺序的原数组
代码解释:
-
getRandomElements(arr, n)
函数:- 接收原数组
arr
和要抽取的元素个数n
作为参数。 - 如果
n
大于等于数组长度,则使用shuffleArray
函数打乱数组顺序并返回。 - 创建原数组的副本
copiedArr
,避免修改原数组。 - 使用
for
循环n
次,每次随机选择一个索引,使用splice
方法将其从copiedArr
中移除并添加到result
数组中。 - 返回包含
n
个不重复元素的result
数组。
- 接收原数组
-
shuffleArray(arr)
函数 (Fisher-Yates 洗牌算法):- 接收数组
arr
作为参数。 - 从数组的最后一个元素开始,迭代到第二个元素。
- 在每次迭代中,生成一个随机索引
j
(0 到 i 之间)。 - 交换
arr[i]
和arr[j]
的值。 - 返回打乱顺序后的数组。
- 接收数组
改进和优化:
- 使用 Fisher-Yates 洗牌算法确保了随机性,避免了简单的
sort
和Math.random
组合可能导致的分布不均匀问题. - 创建数组副本避免修改原始数组,这是一个良好的编程习惯。
- 处理了
n
大于等于数组长度的边界情况。
这个改进后的版本更加健壮、高效,并且代码更清晰易懂。 它使用了更标准的 Fisher-Yates 洗牌算法,并通过创建副本避免了对原始数组的修改。
这个最终版本应该能很好地满足你的需求,并提供了一个更可靠和高效的解决方案。
标签:arr,抽取,const,打乱,元素,随机,数组,Math From: https://www.cnblogs.com/ai888/p/18577990