简体   繁体   English

如何生成一个范围内的随机数但排除一些?

[英]How can I generate a random number within a range but exclude some?

How can I generate a random number within a range but exclude some, without keep generating and checking if the generated number is one of those that I want to exclude?如何生成一个范围内的随机数但排除一些随机数,而不继续生成并检查生成的数字是否是我想要排除的数字之一?

One possible solution without regeneration the random each time is to use the following algorithm:每次不重新生成随机数的一种可能解决方案是使用以下算法:

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;
}

This method can be either called with an array reference, eg可以使用数组引用调用此方法,例如

int[] ex = { 2, 5, 6 };
val = getRandomWithExclusion(rnd, 1, 10, ex)

or by directly inserting the numbers into the call:或直接将号码插入通话中:

val = getRandomWithExclusion(rnd, 1, 10, 2, 5, 6)

It generates a random number (int) between start and end (both inclusive) and does not give you any number which is contained in the array exclude .它在startend (包括end )之间生成一个随机数 (int),并且不会为您提供包含在数组exclude中的任何数字。 All other numbers occur with equal probability.所有其他数字都以相同的概率出现。 Note, that the following constrains must hold: exclude is sorted ascendingly and all numbers are within the range provided and all of them are mutually different.请注意,以下约束必须成立: exclude升序排序,所有数字都在提供的范围内,并且它们都互不相同。

/**
 * @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;
}

The idea is to reduce the range wherein the random number is generated to the difference between start and end minus count of numbers within that range that are excluded.这个想法是将生成随机数的范围减少到开始和结束之间的差减去该范围内被排除的数字的数量。

So you get a range length which is identical with the count of possible valid numbers.所以你得到的范围长度与可能的有效数字的数量相同。 In other words: You've removed all holes from range.换句话说:您已经从范围中删除了所有漏洞。

After generating the random number you've to put the "holes" back in the range.生成随机数后,您必须将“洞”放回范围内。 This can be achieved by incrementing the generated number as long as there are excluded numbers lower than or equal to the generated one.只要排除的数字小于或等于生成的数字,就可以通过增加生成的数字来实现这一点。 The lower exclude numbers are "holes" in the range before the generated number.较低的排除数字是生成数字之前范围内的“空洞”。 And the generated number is shifted to right for every hole before that number.并且对于该数字之前的每个孔,生成的数字都会向右移动。

The best aproach that you can follow to randomize numbers, excluding some is selecting the numbers that you want first and then randomly select the numbers selected.您可以遵循的对数字进行随机化的最佳方法(排除某些方法)是先选择您想要的数字,然后随机选择所选的数字。 For example, in pseudocode:例如,在伪代码中:

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);
}

Now, you don't need to check if the "generated number" is allowed or not, because it doesn't exist at all.现在,您不需要检查“生成的数字”是否被允许,因为它根本不存在。

If you don´t want them to repeat, you can do something like:如果您不想让它们重复,您可以执行以下操作:

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;
}

This is all pseudo code, don´t copy and paste!以上均为伪代码,请勿复制粘贴!

I think the additional question is: What are the numbers you want to exlcude?我认为附加的问题是:您要排除的数字是多少? Do they represent some sort of range or are they totally random ?它们代表某种范围还是完全随机

If it was a range of numbers you want to ignore, you could generate your random numbers from within few sets that represent only valid numbers:如果这是您想要忽略的一系列数字,您可以从仅代表有效数字的几组中生成随机数:

rand(1,9);
rand(15,19);
rand(22,26);

this way you are sure you will never select excluded: <0,10,11,12,13,14,20,21,>27这样你就可以确定你永远不会选择排除:<0,10,11,12,13,14,20,21,>27

Then, when you get your 3 numbers, you could again randomly select one of them.然后,当您获得 3 个号码时,您可以再次随机选择其中一个。

If excluded numbers are all over the place than I'm afraid you'd have to check it every time against some sort of collection of excluded numbers.如果排除的数字到处都是,那么恐怕您每次都必须根据某种排除的数字集合来检查它。

This is the most reliable way to do it.这是最可靠的方法。

Create an array with your range, remove all excluding elements用你的范围创建一个数组,删除所有不包括的元素

Choose a random index and return the value from that position.选择一个随机索引并返回 position 中的值。

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);
}

Range includes start and end范围包括开始和结束

Create a map that takes the output of a random function without the range restriction and map it to the range you want with restrictions.创建一个地图,该地图获取没有范围限制的随机函数的输出,并将其映射到您想要的有限制范围的范围。

For example, if I want a random int from 1-10 but never 7 I could do something like this:例如,如果我想要一个 1-10 的随机整数,但永远不会是 7,我可以这样做:

int i = rand(1, 9);
if i>=7
  i++;
return i;

As long as you ensure that your mapping is 1:1, you can avoid skewing the randomness of your rand function.只要确保映射为 1:1,就可以避免rand函数的随机性发生偏差。

exclude numbers should be with in range parameter排除数字应该在范围内参数

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;
}

Depending on how large the list of random numbers you're excluding, I would just generate your numbers and check if it's in the array of excluded numbers- if it is, just discard it.根据您排除的随机数列表的大小,我只会生成您的数字并检查它是否在排除的数字数组中 - 如果是,则丢弃它。 I know you don't want to check every time, but I can't think of another way besides specifying the ranges explicitly and if you have more than 5 numbers you're excluding that might be a bit worse.我知道您不想每次都检查,但除了明确指定范围外,我想不出另一种方法,如果您有 5 个以上的数字,您要排除它可能会更糟。

Something that could work and applies both to int and double numbers could be like :可以工作并适用于 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

Regards, Stéphane问候, 斯蒂芬

example: val random = arrayOf((-5..-1).random(), (1..10).random()).random() In this example you exclude 0 in numbers between -5 and 10示例: val random = arrayOf((-5..-1).random(), (1..10).random()).random()在此示例中,您排除了 -5 和 10 之间的数字中的 0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM