生成1-99无重复随机数时代码出现栈溢出问题求助
问题原因分析
这个问题我太熟悉了——你的代码在acum集合接近装满的时候会陷入近乎无限的循环,最终导致栈溢出或者程序卡死,核心问题有两个:
List.Contains()的低效性:如果acum是List<int>,Contains()方法会遍历整个列表查找元素,当列表元素很多时,每次检查的耗时会越来越长。- 高概率重复的循环陷阱:Unity的
Random.Range(int min, int max)是左闭右开逻辑,所以你写的Random.Range(1,99)实际生成的是1到98的数字(共98个)。当acum里的元素接近98个时,每次随机到已存在数字的概率会极高——比如已有97个元素时,命中重复值的概率是97/98,循环会一直跑,直到“撞大运”抽到唯一剩下的数字,这个过程会占用大量CPU资源,最终引发栈溢出或程序无响应。
解决方案
推荐三种方案,按优先级从高到低排列:
方案1:洗牌法(最优解)
直接先生成所有可能的数字,打乱顺序后依次取用,从根本上避免重复检查的问题,效率最高,适合需要生成全部或大部分不重复数字的场景。
// 初始化阶段:生成所有候选数字并洗牌 List<int> numberPool = Enumerable.Range(1, 98).ToList(); // 生成1到98的数字 System.Random rng = new System.Random(); int poolCount = numberPool.Count; // Fisher-Yates 洗牌算法,高效打乱列表 while (poolCount > 1) { poolCount--; int randomIndex = rng.Next(poolCount + 1); // 交换元素完成洗牌 int temp = numberPool[randomIndex]; numberPool[randomIndex] = numberPool[poolCount]; numberPool[poolCount] = temp; } // 每次需要取新数字时 if (numberPool.Count > 0) { int newNumb = numberPool[0]; numberPool.RemoveAt(0); // 取出后从池中移除,彻底避免重复 acum.Add(newNumb); // 如果你需要保留到acum集合里 } else { // 处理没有可用数字的情况,比如提示或返回默认值 Debug.Log("没有更多不重复的数字了!"); }
方案2:优化原有循环+使用HashSet
如果你不想用洗牌法,可以对原有代码做两个关键优化:
- 把
acum从List<int>改成HashSet<int>,因为HashSet.Contains()是O(1)时间复杂度,比List的O(n)快得多。 - 添加最大尝试次数,防止无限循环。
// 把acum定义为HashSet,而不是List HashSet<int> acum = new HashSet<int>(); // 生成新数字的逻辑 int newNumb = Random.Range(1, 99); const int maxAttempts = 1000; // 设置一个合理的最大尝试次数 int attempts = 0; while (acum.Contains(newNumb) && attempts < maxAttempts) { newNumb = Random.Range(1, 99); attempts++; } if (attempts >= maxAttempts) { // 没有可用数字了,抛出异常或做相应处理 throw new InvalidOperationException("已没有可用的不重复数字!"); } acum.Add(newNumb);
方案3:仅添加最大尝试次数(不推荐)
如果暂时不想改集合类型,至少要给循环加一个最大尝试次数的限制,避免无限循环:
int newNumb= Random.Range(1, 99); const int maxAttempts = 1000; int attempts = 0; if(acum.Count > 0) { while (acum.Contains(newNumb) && attempts < maxAttempts) { newNumb= Random.Range(1, 99); attempts++; } if (attempts >= maxAttempts) { Debug.LogError("无法生成不重复数字,已达最大尝试次数"); return; // 或做其他处理 } } acum.Add(newNumb);
注意:这个方案在
acum元素较多时仍然会有性能问题,只是避免了无限循环。
内容的提问来源于stack exchange,提问作者Nogerad
相关产品推荐
相关产品推荐

