如何生成具有多个范围的随机整数?

How to generate random integers with multiple ranges?

我 运行 在从不同的 (a,b) 集合生成 X 数量的随机整数时感到困惑。例如,我想生成来自 (1,5)、(9,15) 和 (21,27) 的 5 个随机整数。我的代码生成 5 个随机整数,但仅在 21 到 27 之间,而不是其他两个。理想情况下,我希望看到类似 1,4,13,22,25 而不是 21,21,25,24,27 的内容。

我的代码:

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

不理想,但它有效。

首先它从所有范围中获取随机数,然后它(随机地)选择一个值。

from random import randint, choice

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

正如 Blender 所说 - 更清晰的版本 - 首先它(随机)选择一个范围,然后从该范围中获取随机值。

from random import randint, choice

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

这个 foor 循环将生成 3 个随机数,一个来自提供的第一个范围,另一个用于第二个范围,最后一个用于最后一个范围,并在输出中打印每个随机数。 你的打印不在 foor 循环中,只打印最后给定范围内的最后一个随机数。

对于不重复的号码:

from random import randint, choice

randoms = []
counter = 0    
while True:
   new_random = (choice([randint(1,5),randint(9,15),randint(21,27)]))
   if new_random not in randoms:
      randoms.append(new_random)
      counter += 1
   if counter == 5 :
      break
print randoms

这是个有趣的问题;当您意识到要实现真正的随机性,选择特定范围的概率必须由该范围的长度来权衡时,这就变得很有趣了。

等长范围:

如果三个范围长度相等,比如range(0, 10),range(20, 30)和range(40, 50);然后,要选择一个随机数,我们可以执行以下操作:

  1. 随机选择一个范围。
  2. 从该范围中选择一个随机数。

不等长范围:

现在,考虑三个大小不等的范围,比如 range(0, 2)、range(4, 6) 和 range(10, 100);

第三个范围比前两个大很多。如果我们采用与处理同样长的范围时采用的相同策略,我们将偏向于从前两个范围中挑选数字。

为了从三个不等长的范围中挑选出真随机数,有两种策略。

策略一:利用概率

选择一个范围的概率应该使得选择一个数字的概率保持不变。我们可以通过权衡短距离击球的可能性来实现这一点。

但是,不是计算概率权重;有更好的解决方案。参见策略 2。

策略 2:合并范围

我们可以简单地将三个范围合并为一个范围。然后,从合并范围中随机选择一个数字。很简单:

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;

让我们看看实际效果:

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

randomPicker 的另一个好处是它可以处理任意数量的范围。

这里有一些有趣的答案,虽然我认为这个问题可以用更少的代码来解决,即使它的可读性稍差一点。

这里的基本思想是你有固定数量的选择,所以你基本上可以有一个范围来完成这项工作,然后将结果绘制到你想要的范围内。或者,如果您想换一种方式考虑它,请创建一个函数 f(x) -> y 归结为同一件事。

from random import randint

for i in xrange(5):
    n = randint(1,19)
    if n > 12:
        print(n + 8)
    elif n > 5:
        print(n + 3)
    else:
        print(n)

或者,使用函数:

from random import randint

def plot_to_ranges(n):
    if n > 12:
        return n + 8
    elif n > 5:
        return n + 3
    else:
        return n

for i in xrange(5):
    n = randint(1,19)
    print(plot_to_ranges(n))

如果您使用 Python 3.x,您应该将 xrange 更改为 range

import itertools, random
nums = list(itertools.chain(
            range(1,5),
            range(9,15),
            range(21,27)))
random.choices(nums, k=5)

Sumukh Barve 回答的补充:

策略 3:选择一个不连接范围的范围成员

此方法对指定范围的长度求和。然后它会在该范围内生成一个随机整数。然后它根据它相对于范围的位置添加到该整数。

示例: 如果范围是 (1,3) 和 (5,9),其中范围包含在内。将所有范围的长度相加得到 3+4=7。然后我们创建一个介于 1 和 7(或 0 和 6)之间的随机整数。比方说我们得到 2,我们从第一个范围中选择第二个项目。如果我们得到 5,我们从第二个范围中选择第二个项目。

def randint_within_ranges(ranges):
    """ranges is a list of tuples, where each tuple is a range. E.g. [(0, 2), (4, 6), (10, 100)]. 
    Ranges must be non-overlapping. 
    Ranges are inclusive (e.g. can return 4 or 6).
    Returns a random int anywhere in any of those ranges. 
    (In this example, high probability will be from (10,100) range.)
    """
    #in case ranges aren't ordered, sort them now. Lowest first, e.g. [(0, 2), (4, 6), (10, 100)]
    ranges = sorted(ranges, key=min)
    #print('ranges',ranges)
    total_range_size = sum([abs(range_[1]-range_[0])+1 for range_ in ranges])
    #print('total_range_size',total_range_size)
    lowest_range_start = min([min(range_) for range_ in ranges])
    #print('lowest_range_start',lowest_range_start)
    randint = random.randint(0, total_range_size-1) 
    #print('randint',randint)
    randint += lowest_range_start
    #print('randint',randint)

    #add the gaps
    for i in range(len(ranges)-1):
        if randint > max(ranges[i]):
            randint += (min(ranges[i+1])-max(ranges[i])-1)#gaps[i]
        
    return randint

print(  randint_within_ranges([ (4, 6), (-300, -290), (285,289), (10, 15), (48,53), (85,91)])  )

您可以使用以下方法测试范围内的 returns 个数字。范围包括两个界限。

randints = set()
for i in range(1000): 
    randints.add(randint_within_ranges([ (4, 6), (-300, -290), (285,289), (10, 15), (48,53), (85,91)]))
print(len(randints), sorted(list(randints)))