feat(utils): 添加安全洗牌与随机抽样函数 Feature #91
This commit is contained in:
53
src/views/Home/utils/random.ts
Normal file
53
src/views/Home/utils/random.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* 浏览器端加密安全洗牌(无需指定抽取数量)
|
||||||
|
* @param array 要洗牌的数组
|
||||||
|
* @returns 洗牌后的新数组
|
||||||
|
*/
|
||||||
|
function shuffleBrowserCrypto<T>(array: T[]): T[] {
|
||||||
|
const newArray = [...array]
|
||||||
|
if (newArray.length <= 1)
|
||||||
|
return newArray
|
||||||
|
|
||||||
|
// 遍历数组,每轮生成一个随机索引
|
||||||
|
for (let i = newArray.length - 1; i > 0; i--) {
|
||||||
|
// 步骤1:生成一个 32 位无符号加密随机数(仅需1个)
|
||||||
|
const randomBuffer = new Uint32Array(1) // 长度1表示只生成1个随机数
|
||||||
|
crypto.getRandomValues(randomBuffer)
|
||||||
|
|
||||||
|
// 步骤2:将随机数映射到 [0, i] 范围(核心:动态适配当前i的范围)
|
||||||
|
const randomIndex = randomBuffer[0] % (i + 1);
|
||||||
|
|
||||||
|
// 步骤3:交换元素
|
||||||
|
[newArray[i], newArray[randomIndex]] = [newArray[randomIndex], newArray[i]]
|
||||||
|
}
|
||||||
|
return newArray
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 从源数组中随机获取指定数量的元素
|
||||||
|
* @param {Array} sourceArray 源数组
|
||||||
|
* @param {number} count 要获取的元素数量
|
||||||
|
* @returns {Array} 随机获取的元素
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function getRandomElements<T>(sourceArray: T[], count: number): T[] {
|
||||||
|
if (count <= 0)
|
||||||
|
return []
|
||||||
|
if (count >= sourceArray.length) {
|
||||||
|
return shuffleBrowserCrypto([...sourceArray])
|
||||||
|
} // 抽全部=洗牌
|
||||||
|
|
||||||
|
const newArray = [...sourceArray]
|
||||||
|
const result: T[] = []
|
||||||
|
|
||||||
|
// 抽取 count 个元素,每轮选一个随机索引加入结果,然后从原数组移除
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const randomBuffer = new Uint32Array(1)
|
||||||
|
crypto.getRandomValues(randomBuffer)
|
||||||
|
const randomIndex = randomBuffer[0] % newArray.length
|
||||||
|
|
||||||
|
result.push(newArray[randomIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user