【发布时间】:2011-09-20 13:16:45
【问题描述】:
如何生成一个范围内的随机数但排除一些,而不继续生成并检查生成的数字是否是我想要排除的数字之一?
【问题讨论】:
-
你想要一个 int 或 double 随机数?
-
您的范围内是否有太多排除项,以至于很有可能达到排除项?
如何生成一个范围内的随机数但排除一些,而不继续生成并检查生成的数字是否是我想要排除的数字之一?
【问题讨论】:
每次不重新生成随机数的一种可能解决方案是使用以下算法:
public int getRandomWithExclusion(Random rnd, int start, int end, int... exclude) {
int random = start + rnd.nextInt(end - start + 1 - exclude.length);
for (int ex : exclude) {
if (random < ex) {
break;
}
random++;
}
return random;
}
可以使用数组引用调用此方法,例如
int[] ex = { 2, 5, 6 };
val = getRandomWithExclusion(rnd, 1, 10, ex)
或直接在通话中插入号码:
val = getRandomWithExclusion(rnd, 1, 10, 2, 5, 6)
它会在start 和end(包括两者)之间生成一个随机数(int),并且不会为您提供数组exclude 中包含的任何数字。所有其他数字都以相同的概率出现。请注意,必须满足以下约束条件:exclude 按升序排序,并且所有数字都在提供的范围内,并且它们都相互不同。
【讨论】:
random < ex,然后立即返回random。否则执行random++ 并继续循环。应该在大型排除数组上表现更好。
do{ draw=start+rnd.nextInt(end);} while (Arrays.binarySearch(ex, draw) >=0);,发现有时搜索数组更快。随着排除与池的比率接近 1(例如,在 11 个池中排除 10 个数字),搜索数组变得非常慢(1 亿次迭代需要 56 秒,而使用上述方法需要 3.5 秒)但是如果将池扩展到 1000,例如搜索数组更快(4.1s vs 4.7s)。再一次,这样做了 1 亿次。上述单次大约需要 10000 纳秒,而搜索需要 20000 纳纳。
/**
* @param start start of range (inclusive)
* @param end end of range (exclusive)
* @param excludes numbers to exclude (= numbers you do not want)
* @return the random number within start-end but not one of excludes
*/
public static int nextIntInRangeButExclude(int start, int end, int... excludes){
int rangeLength = end - start - excludes.length;
int randomInt = RANDOM.nextInt(rangeLength) + start;
for(int i = 0; i < excludes.length; i++) {
if(excludes[i] > randomInt) {
return randomInt;
}
randomInt++;
}
return randomInt;
}
这个想法是将生成随机数的范围缩小到开始和结束之间的差减去该范围内被排除的数字的计数。
因此,您得到的范围长度与可能的有效数字的计数相同。换句话说:你已经从范围内移除了所有的洞。
生成随机数后,您必须将“孔”放回范围内。只要排除的数字小于或等于生成的数字,就可以通过增加生成的数字来实现这一点。较低的排除数字是生成数字之前范围内的“洞”。对于该数字之前的每个洞,生成的数字都会向右移动。
【讨论】:
您可以遵循随机化数字的最佳方法,其中一些方法是先选择您想要的数字,然后随机选择所选数字。例如,在伪代码中:
List<Number> numbers;
numbers.add(1);
numbers.add(2);
numbers.add(3);
//You can do a "for" without adding the excluded numbers..
//Then, your randomizer could be...
public Number getRandoNumber() {
int index = Random.get(0, numbers.size());
return numbers.get(index);
}
现在,您不需要检查“生成的数字”是否允许,因为它根本不存在。
如果您不希望它们重复,您可以执行以下操作:
Collections.shuffle(numbers);
public Number getRandomNotRepeat() {
if(numbers.size() == 0)
throw new RuntimeException("No more numbers");
Number n = numbers.get(0);
numbers.removeFirst();
return n;
}
这都是伪代码,不要复制粘贴!
【讨论】:
我认为额外的问题是:您要排除哪些数字?它们代表某种范围还是完全随机?
如果您想忽略一系列数字,您可以从仅代表有效数字的几组中生成随机数:
rand(1,9);
rand(15,19);
rand(22,26);
这样你就确定你永远不会选择排除:27
然后,当你得到你的 3 个号码时,你可以再次随机选择其中一个。
如果排除的数字到处都是,恐怕你每次都必须对照某种排除数字的集合来检查它。
【讨论】:
创建一个不带范围限制的随机函数输出的映射,并将其映射到您想要的带限制范围内。
例如,如果我想要一个从 1 到 10 的随机整数,但从不想要 7,我可以这样做:
int i = rand(1, 9);
if i>=7
i++;
return i;
只要确保映射为 1:1,就可以避免扭曲 rand 函数的随机性。
【讨论】:
根据您排除的随机数列表的大小,我会生成您的数字并检查它是否在排除的数字数组中 - 如果是,则丢弃它。我知道您不想每次都检查,但除了明确指定范围之外,我想不出其他方法,如果您排除的数字超过 5 个,那可能会更糟。
【讨论】:
可以工作并适用于 int 和 double 数字的东西可能像:
public int getRandomNumberWithExclusion( int start, int end )
{
Random r = new Random();
int result = -1;
do
{
result = start + r.nextInt( end - start );
}//do
while( !isAllowed( result ) );
return result;
}//met
private boolean isAllowed( int number )
{
//your test for restricted values here
}//met
问候, 斯蒂芬
【讨论】:
排除数字应带有范围参数
private int GiveMeANumber(int range,int... exclude)
{
Set<Integer> integers=new HashSet<>();
int count=range;
for(int i=0;i<count;i++)
integers.add(i);
integers.removeAll(Arrays.asList(exclude));
int index = new Random().nextInt(range - exclude.length);
count=0;
for (int value:integers){
if(count==index)
return value;
count++;
}
return 0;
}
【讨论】:
示例:val random = arrayOf((-5..-1).random(), (1..10).random()).random()
在此示例中,您排除了介于 -5 和 10 之间的数字中的 0
【讨论】:
这是最可靠的方法。
使用您的范围创建一个数组,删除所有排除元素
选择一个随机索引并从该位置返回值。
public int getRandomNumber(int start, int end, List<Integer> excludingNumbers) {
int[] range = IntStream.rangeClosed(start, end).toArray();
List<Integer> rangeExcluding = Arrays.stream( range ).boxed().collect( Collectors.toList() );
rangeExcluding.removeAll(list);
int newRandomInt = new Random().nextInt(rangeExcluding.size())
return rangeExcluding.get(newRandomInt);
}
范围包括开始和结束
【讨论】: