JS生成9长度随机倍率数组时触发无限循环的问题排查
生成随机奖券数组时触发无限循环问题修复
问题说明
需求逻辑:
- 从倍率池
["x10", "x20", "x30", "x40", "x50", "x60"]中随机选出3个不重复的倍率值 - 最终生成长度为9的数组,选中的3个倍率每个恰好出现3次,位置随机排布
- 实际运行编写的代码时进入无限循环,无法输出预期结果
无限循环触发原因
核心bug为第二个填充数组的for循环终止条件写错:
- 需要的数组总长度为9,但循环终止条件写的是
i < 10,逻辑上会尝试完成10次有效元素插入 - 当数组已经填充到9个元素(3个倍率各3个)时,i会自增到9,满足
i < 10的循环进入条件,此时无论随机到哪个倍率,push后该倍率的计数都会达到4,触发pop()+i--的回退逻辑:- pop移除刚加入的元素,数组长度回到9
- i被减为8,本轮循环结束后i自增重回9
- 循环判断条件永远成立,代码卡在该步骤无限重复执行
除此之外,原有的随机插入逻辑即使修改了循环终止条件,也存在性能不稳定的问题:当数组只剩最后1个空位、且仅对应1个未满额的倍率时,随机选值有2/3概率选到已满额的倍率,会反复触发回退逻辑,极端情况下会卡顿很久才能完成填充。
优化实现方案
更稳妥的实现思路是先生成符合数量要求的元素集合,再通过Fisher-Yates洗牌算法打乱顺序,完全避免循环回退、卡住的问题:
const victoryMultipliers = ["x10", "x20", "x30", "x40", "x50", "x60"]; // 随机选3个不重复的倍率 const threeTicketArray = []; while (threeTicketArray.length < 3) { const randVal = victoryMultipliers[Math.floor(Math.random() * victoryMultipliers.length)]; if (!threeTicketArray.includes(randVal)) { threeTicketArray.push(randVal); } } // 生成每个倍率恰好出现3次的基础数组 const ticketArray = threeTicketArray.flatMap(multiplier => Array(3).fill(multiplier)); // 洗牌算法随机打乱数组顺序 for (let i = ticketArray.length - 1; i > 0; i--) { const randomIndex = Math.floor(Math.random() * (i + 1)); [ticketArray[i], ticketArray[randomIndex]] = [ticketArray[randomIndex], ticketArray[i]]; } console.log(ticketArray);
如果要沿用原本的循环插入逻辑,仅需要把第二个for循环的终止条件从i < 10修改为i < 9即可解决无限循环问题,但不推荐这种写法,稳定性较差。
内容的提问来源于stack exchange,提问作者Mohamed
相关产品推荐
相关产品推荐

