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

时间:2022-02-18 18:58:15

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?

如何生成一个范围内的随机数但排除一些,而不继续生成并检查生成的数字是否是我要排除的数字之一?

8 个解决方案

#1


40  

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, e.g.

可以使用数组引用调用此方法,例如,

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. 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.

它在开始和结束(包括两者)之间生成一个随机数(int),并且不会为您提供数组排除中包含的任何数字。所有其他数字的概率相等。请注意,必须遵守以下约束:排除按升序排序,所有数字都在提供的范围内,并且所有数字都是相互不同的。

#2


15  

/**
 * @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.

生成随机数后,您需要将“孔”放回范围内。只要排除的数字低于或等于生成的数字,就可以通过递增生成的数字来实现这一点。较低的排除数字是在生成的数字之前的范围内的“洞”。并且在该数字之前,生成的数字向右移动。

#3


3  

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!

这是所有伪代码,不要复制和粘贴!

#4


2  

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.

如果排除的数字到处都是,我担心你每次都要对某些被排除的数字集合进行检查。

#5


1  

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的随机int但从来没有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函数的随机性。

#6


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个数字,你排除可能会有点差。

#7


0  

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

#8


0  

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

#1


40  

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, e.g.

可以使用数组引用调用此方法,例如,

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. 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.

它在开始和结束(包括两者)之间生成一个随机数(int),并且不会为您提供数组排除中包含的任何数字。所有其他数字的概率相等。请注意,必须遵守以下约束:排除按升序排序,所有数字都在提供的范围内,并且所有数字都是相互不同的。

#2


15  

/**
 * @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.

生成随机数后,您需要将“孔”放回范围内。只要排除的数字低于或等于生成的数字,就可以通过递增生成的数字来实现这一点。较低的排除数字是在生成的数字之前的范围内的“洞”。并且在该数字之前,生成的数字向右移动。

#3


3  

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!

这是所有伪代码,不要复制和粘贴!

#4


2  

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.

如果排除的数字到处都是,我担心你每次都要对某些被排除的数字集合进行检查。

#5


1  

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的随机int但从来没有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函数的随机性。

#6


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个数字,你排除可能会有点差。

#7


0  

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

#8


0  

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