How to generate random integers with multiple ranges?

11,726

Solution 1

Not ideal but it works.

First it gets random numbers from all ranges and next it selects (randomly) one value.

from random import randint, choice

for _ in range(5):
    print(choice([randint(1,5),randint(9,15),randint(21,27)]))

As Blender said - cleaner version - first it selects (randomly) one range and later it gets random value from this range.

from random import randint, choice

for _ in range(5):
    r = choice([(1,5),(9,15),(21,27)])
    print(randint(*r))

Solution 2

This is an interesting question; it becomes interesting when you realize that to achieve true randomness, the probability of picking a particular range must be weighed by the length of that range.

Ranges of Equal Length:

If the three ranges are of equal length, say range(0, 10), range(20, 30) and range(40, 50); then, to pick a single random number, we may do the following:

  1. Pick a range at random.
  2. Pick a random number from that range.

Ranges of Unequal Length:

Now, consider three of unequally sized ranges, say range(0, 2), range(4, 6) and range(10, 100);

The third range is much larger than the first two. If we employ the same strategy we employed in dealing with equally long ranges, we will be biased towards picking numbers from the first two ranges.

In order to pick truly random numbers from the three unequally long ranges, there are two strategies.

Strategy 1: Using probability

The probability of picking a range should be such that the probability of picking a number remains the same. We could accomplish this by weighing down the probability of piking shorter ranges.

However, instead of computing probability weights; there's a better solution. See Strategy 2.

Strategy 2: Merging the ranges

We could simply merge the three ranges into a single single range. Then, randomly pick a number from the merged range. It's simple:

import random;
def randomPicker(howMany, *ranges):
    mergedRange = reduce(lambda a, b: a + b, ranges);
    ans = [];
    for i in range(howMany):
        ans.append(random.choice(mergedRange));
    return ans;

Let's see it in action:

>>> randomPicker(5, range(0, 10), range(15, 20), range(40, 60));
[47, 50, 4, 50, 16]
>>> randomPicker(5, range(0, 10), range(70, 90), range(40, 60));
[0, 9, 55, 46, 44]
>>> randomPicker(5, range(0, 10), range(40, 60));
[50, 43, 7, 42, 4]
>>> 

An added benefit of randomPicker is that it can deal with any number of ranges.

Solution 3

import itertools, random
nums = list(itertools.chain(
            range(1,5),
            range(9,15),
            range(21,27)))
random.choices(nums, k=5)
Share:
11,726
Admin
Author by

Admin

Updated on July 22, 2022

Comments

  • Admin
    Admin almost 2 years

    I've run into confusion in generating X amount of random integers from different sets of (a,b). For example, I would like to generate 5 random integers coming from (1,5), (9,15),and (21,27). My code generates 5 random integers but only between 21 and 27 and not from the other two. Ideally I'd like to see something like 1,4,13,22,25 instead of 21,21,25,24,27.

    My code:

    from random import randint
    n = 0
    while n < 5:
        n += 1
        for i in (randint(1,5),randint(9,15),randint(21,27)):
            x = i
        print i
    
  • Blender
    Blender over 8 years
    random.randint(*random.choice([(1, 5), (9, 15), (21, 27)])) is a little clearer.
  • furas
    furas over 8 years
    @Blender I was working on this version too, but you are faster :)
  • Sumukh Barve
    Sumukh Barve over 8 years
    @Blender: When dealing with ranges of unequal length, this solution is biased towards shorter ranges. See my answer to see what I mean.