Java中ThreadLocalRandom生成5-13随机数易重复,如何降低重复率?
首先得明确:你当前用的nextInt(5,13)生成的是5到12之间的整数(Java的nextInt(origin, bound)是左闭右开区间),如果要包含13的话,得改成nextInt(5,14)。不过不管是8个还是9个可选值,范围太小确实容易出现重复——毕竟随机数本身就有概率重复,尤其是可选值不多的时候。
下面给你几个实用的解决方案,按需选择:
方案1:洗牌池法(无重复直到耗尽所有值)
这个方法能保证在所有可选值都被用过一遍之前,不会出现重复。原理是把所有可能的数值放进一个列表,打乱顺序后逐个取出,取完了再重新打乱循环。
代码示例:
import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.ThreadLocalRandom; // 把这些变量放在你的类里作为成员变量 private List<Integer> levelPool; private Iterator<Integer> levelIterator; private int getNextLevel() { // 如果迭代器为空或者已经取完所有值,重新初始化并洗牌 if (levelIterator == null || !levelIterator.hasNext()) { // 按需调整数值范围,要包含13就添加进去 levelPool = Arrays.asList(5, 6, 7, 8, 9, 10, 11, 12); // 用ThreadLocalRandom洗牌,保证线程安全 Collections.shuffle(levelPool, ThreadLocalRandom.current()); levelIterator = levelPool.iterator(); } return levelIterator.next(); }
优点:完全避免重复直到一轮结束,数值分布绝对均匀;线程安全。
缺点:如果你的场景需要完全随机(允许跨轮重复),这个方法会让随机性稍受限制,但大部分游戏或业务场景都适用。
方案2:避免连续重复
如果只是不想连续出现相同的值,这个方法最简单:记录上一次生成的数值,新生成的如果和上次一样就重新生成。
代码示例:
import java.util.concurrent.ThreadLocalRandom; private int lastLevel = -1; // 初始化一个不在范围内的值 private int getNextLevel() { int nextLevel; do { nextLevel = ThreadLocalRandom.current().nextInt(5, 13); // 注意区间是否符合需求 } while (nextLevel == lastLevel); lastLevel = nextLevel; return nextLevel; }
优点:代码极简,几乎不影响性能;保留了大部分随机性。
缺点:只能避免连续重复,还是可能出现非连续的重复(比如5→6→5),但已经能解决你说的"频繁生成相同值"的问题。
方案3:加权随机(降低近期值的概率)
如果想要更进一步,让刚出现过的值出现概率降低,可以给每个数值设置权重,最近出现过的权重调低,过一段时间再恢复。这个方法稍微复杂一点,但灵活性更高。
大致思路:
- 维护一个Map记录每个数值的权重(初始都是1)
- 每次生成随机数时,根据权重进行抽样
- 生成后,把当前数值的权重调低,同时恢复上一次数值的权重
代码示例(简化版):
import java.util.HashMap; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; private Map<Integer, Double> weightMap = new HashMap<>(); private int lastLevel = -1; // 初始化权重 { for (int i = 5; i < 13; i++) { weightMap.put(i, 1.0); } } private int getNextLevel() { // 计算总权重 double totalWeight = weightMap.values().stream().mapToDouble(Double::doubleValue).sum(); double random = ThreadLocalRandom.current().nextDouble(totalWeight); double currentSum = 0; int selected = -1; for (Map.Entry<Integer, Double> entry : weightMap.entrySet()) { currentSum += entry.getValue(); if (random <= currentSum) { selected = entry.getKey(); break; } } // 重置上一次的权重,降低当前选中值的权重 if (lastLevel != -1) { weightMap.put(lastLevel, 1.0); } weightMap.put(selected, 0.3); // 把当前值的权重降到30% lastLevel = selected; return selected; }
优点:能有效减少近期重复的概率,随机性更符合"自然"的感觉。
缺点:实现稍复杂,需要维护权重逻辑;性能比前两个方案略差,但对于小范围数值来说可以忽略。
最后提醒:如果你的业务场景允许,适当扩大数值范围(比如从5到15)也能从根本上降低重复概率,但这得看你的需求是否允许。
内容的提问来源于stack exchange,提问作者user8716561

