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

9
我在生成来自不同(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
7个回答

14

不是最理想的,但它可以工作。

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

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

6
random.randint(*random.choice([(1, 5), (9, 15), (21, 27)])) 这段代码稍微清晰了一些。 - Blender
@Blender 我也在做这个版本,但你更快 :) - furas
4
@Blender:处理长度不等的范围时,这个解决方案偏向于较短的范围。请看我的答案,了解我所说的内容。 - Sumukh Barve

9
这是一个有趣的问题,当你意识到要实现真正的随机性时,选择特定范围的概率必须根据该范围的长度加权,这使得它变得更加有趣。
等长的范围:
如果三个范围长度相等,比如范围(0,10),范围(20,30)和范围(40,50);那么,为了选择一个单一的随机数,我们可以采取以下步骤:
1. 随机选择一个范围。 2. 从该范围内随机选择一个数字。
不等长的范围:
现在,考虑三个不等长的范围,例如范围(0, 2),范围(4, 6)和范围(10, 100);
第三个范围比前两个范围要大得多。如果我们采用与处理等长范围相同的策略,我们会偏向于从前两个范围中选择数字。
为了从三个不等长的范围中选择真正的随机数,有两种策略。
策略1:使用概率
选择范围的概率应该使得选择数字的概率保持不变。我们可以通过降低选择较短范围的概率来实现这一点。
然而,除了计算概率权重之外,还有一种更好的解决方案。请参见策略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]
>>> 
< p > randomPicker 的另一个好处是可以处理任意数量的范围。


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

0
for i in (randint(1,5),randint(9,15),randint(21,27)):
    print i

这个 for 循环将生成 3 个随机数,一个来自第一个范围,另一个来自第二个范围,最后一个来自最后一个范围,并将每个数字打印到输出中。 你的打印语句在 for 循环之外,只打印了最后一个给定范围的随机数。

0

对于非重复数字:

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

0

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

基本思路是你有一个固定数量的选择,所以你可以基本上有一个范围来完成工作,然后将结果绘制到你想要的范围。或者如果你想另外考虑,创建一个函数 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

0

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

您可以使用以下方法测试此函数是否仅返回范围内的数字。范围包括两个边界。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接