【问题标题】:How to generate random integers with multiple ranges?如何生成具有多个范围的随机整数?
【发布时间】:2023-03-04 19:11:01
【问题描述】:

我在从不同的 (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

【问题讨论】:

    标签: python random integer


    【解决方案1】:

    不理想,但它有效。

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

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

    【讨论】:

    • random.randint(*random.choice([(1, 5), (9, 15), (21, 27)])) 清晰一点。
    • @Blender 我也在开发这个版本,但你更快:)
    • @Blender:在处理长度不等的范围时,此解决方案偏向于较短的范围。请参阅我的答案以了解我的意思。
    【解决方案2】:

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

    等长范围:

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

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

    长度不等的范围:

    现在,考虑三个大小不等的范围,例如 range(0, 2)、range(4, 6) 和 range(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]
    >>> 
    

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

    【讨论】:

      【解决方案3】:
      import itertools, random
      nums = list(itertools.chain(
                  range(1,5),
                  range(9,15),
                  range(21,27)))
      random.choices(nums, k=5)
      

      【讨论】:

        【解决方案4】:
        for i in (randint(1,5),randint(9,15),randint(21,27)):
            print i
        

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

        【讨论】:

          【解决方案5】:

          对于不重复的数字:

          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
          

          【讨论】:

            【解决方案6】:

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

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

            【讨论】:

              【解决方案7】:

              Sumukh Barve 回答的补充:

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

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

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

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2019-08-06
                • 2015-08-18
                • 1970-01-01
                • 1970-01-01
                • 2013-12-14
                • 2015-10-28
                • 2010-09-22
                相关资源
                最近更新 更多