如何高效生成特定范围内的唯一随机非零整数?

huangapple go评论125阅读模式
英文:

How to efficiently generate unique random non-zero integers from specific spaces in a range?

问题

以下是您要翻译的代码部分:

import random

my_list = []
c = 0
while c < 100:
    if c < 5:
        # Both values are unique.
        first_num, second_num = random.sample(range(-5, 6), 2)
        # Exclusion of 0.
        while first_num == 0 or second_num == 0:
            first_num, second_num = random.sample(range(-5, 6), 2)
        c += 1
    elif 5 <= c < 80:
        first_num, second_num = random.sample(range(-100, 101), 2)
        while first_num == 0 or second_num == 0:
            first_num, second_num = random.sample(range(-100, 101), 2)
        c += 1
    else:
        first_num, second_num = random.sample(range(-150, 151), 2)
        while first_num == 0 or second_num == 0:
            first_num, second_num = random.sample(range(-150, 151), 2)
        c += 1

    random_nums = (first_num, second_num)
    if random_nums not in my_list:
        my_list.append(random_nums)
    else:
        c -= 1

如果您有任何其他需要翻译的内容,请随时提出。

英文:

I want to generate 100 pairs of unique random non-zero integers from the range (-150, 151). But I want my code to generate from specific areas in (-150, 151). I have coded it as follows:

import random

my_list = []
c = 0
while c &lt; 100:
    if c &lt; 5:
        # Both values are unique.
        first_num, second_num = random.sample(range(-5, 6), 2)
        # Exclusion of 0.
        while first_num == 0 or second_num == 0:
            first_num, second_num = random.sample(range(-5, 6), 2)
        c += 1
    elif 5 &lt;= c &lt; 80:
        first_num, second_num = random.sample(range(-100, 101), 2)
        while first_num == 0 or second_num == 0:
            first_num, second_num = random.sample(range(-100, 101), 2)
        c += 1
    else:
        first_num, second_num = random.sample(range(-150, 151), 2)
        while first_num == 0 or second_num == 0:
            first_num, second_num = random.sample(range(-150, 151), 2)
        c += 1

    random_nums = (first_num, second_num)
    if random_nums not in my_list:
        my_list.append(random_nums)
    else:
        c -= 1

Is there a more elegant or efficient way alternative to what I have coded above?

UPDATE

After discussing with MatBailie in the comments, I am updating the requirements:

If the whole selected range is (-150 to 151), then in the final my_list:

  • (5,6), (6,5) are allowed but (5,6), (5,6) are not.
  • If (-1, 1) is generated once from range (-5 to 6) then it should not be generated again from the bigger ranges such as (-100 to 101) or (-150 to 151)
  • It is also good to have pairs such as (-1, 140) or (140, -1).

UPDATE

Benchmarking MatBailie, Alain T. and Samwise's answers on perfpy with 100 pairs, I got the following result:

如何高效生成特定范围内的唯一随机非零整数?

The same benchmark with generating 1000 pairs and the same ratio of areas, I got this:

如何高效生成特定范围内的唯一随机非零整数?

答案1

得分: 2

以下是代码的中文翻译:

# 可以构建一个包含100个范围的列表,以满足所需大小的分组。这将使剩下的逻辑更加通用和简单:

import random

ranges = [range(-5,6)]*5 + [range(-100,101)]*75 + [range(-150,151)]*20
pairs  = []
for R in ranges:
    a = 0
    while not a or not b or (a,b) in pairs:
        a,b = random.sample(R,2)  # random.choices(R,k=2)
    pairs.append((a,b))

# 输出:

print(pairs)

# [(-3, 2), (-3, -5), (3, 1), (-4, -3), (2, 4), (-73, -9), (16, -56),
#  (12, -81), (5, -50), (99, -6), (35, 71), (30, -75), (98, 25), (55, -58), 
#  (73, -24), (-65, 4), (75, -96), (-6, -90), (-80, 22), (-93, -39), 
#  (-69, -48), (-30, 25), (85, -11), (37, 60), (91, 96), (98, 100), 
#  (-100, -54), (58, 20), (-14, -95), (-76, -12), (-5, -84), (70, -53), 
#  (91, -66), (-61, 2), (4, -42), (-15, -70), (-52, -6), (-5, 93), (26, 76),
#  (-8, -79), (63, -7), (-23, -27), (56, 13), (46, 27), (80, 94), (-94, -66), 
#  (-46, -19), (-4, -87), (-92, -48), (24, 32), (10, -89), (-50, -96),   
#  (-5, -85), (-32, 69), (-60, 69), (87, -81), (-100, -91), (87, 37), 
#  (-60, 45), (-70, -84), (-98, 80), (-88, 57), (-67, -44), (-72, 4), 
#  (-76, 5), (-79, -16), (-9, 80), (76, -41), (15, 77), (-47, -1), (58, 12), 
#  (66, -2), (21, 16), (-24, -12), (54, -69), (58, -73), (61, 68), (-89, -37), 
#  (-85, 68), (-7, -21), (-86, -31), (48, 136), (137, 86), (-61, -32), 
#  (104, -144), (-24, -103), (80, 135), (-2, 39), (49, -21), (15, -103), 
#  (-14, -145), (-48, -61), (142, -90), (93, -132), (-68, -111), (-80, 2), 
#  (6, 45), (-89, 75), (-146, -76), (-94, -24)]

# 你也可以只使用一个列表来实现,通过初始化包含范围的结果列表,然后用生成的对替换范围:

pairs = [range(-5,6)]*5 + [range(-100,101)]*75 + [range(-150,151)]*20
for i,R in enumerate(pairs):
    pairs[i] = (0,0)
    while not all(pairs[i]) or pairs.index(pairs[i])&lt;i:
        pairs[i] = random.sample(R,2) # random.choices(R,k=2)

注意:请确保在代码中不要使用HTML实体"lt",而应使用小于号"<"。

英文:

You could build a list of the 100 ranges repeating the same ones in groups of the required size. This will make the rest of the logic more generalized and simpler:

import random
   
ranges = [range(-5,6)]*5 + [range(-100,101)]*75 + [range(-150,151)]*20
pairs  = []
for R in ranges:
    a = 0
    while not a or not b or (a,b) in pairs:
        a,b = random.sample(R,2)  # random.choices(R,k=2)
    pairs.append((a,b))

output:

print(pairs)

[(-3, 2), (-3, -5), (3, 1), (-4, -3), (2, 4), (-73, -9), (16, -56),
 (12, -81), (5, -50), (99, -6), (35, 71), (30, -75), (98, 25), (55, -58), 
 (73, -24), (-65, 4), (75, -96), (-6, -90), (-80, 22), (-93, -39), 
 (-69, -48), (-30, 25), (85, -11), (37, 60), (91, 96), (98, 100), 
 (-100, -54), (58, 20), (-14, -95), (-76, -12), (-5, -84), (70, -53), 
 (91, -66), (-61, 2), (4, -42), (-15, -70), (-52, -6), (-5, 93), (26, 76),
 (-8, -79), (63, -7), (-23, -27), (56, 13), (46, 27), (80, 94), (-94, -66), 
 (-46, -19), (-4, -87), (-92, -48), (24, 32), (10, -89), (-50, -96),   
 (-5, -85), (-32, 69), (-60, 69), (87, -81), (-100, -91), (87, 37), 
 (-60, 45), (-70, -84), (-98, 80), (-88, 57), (-67, -44), (-72, 4), 
 (-76, 5), (-79, -16), (-9, 80), (76, -41), (15, 77), (-47, -1), (58, 12), 
 (66, -2), (21, 16), (-24, -12), (54, -69), (58, -73), (61, 68), (-89, -37), 
 (-85, 68), (-7, -21), (-86, -31), (48, 136), (137, 86), (-61, -32), 
 (104, -144), (-24, -103), (80, 135), (-2, 39), (49, -21), (15, -103), 
 (-14, -145), (-48, -61), (142, -90), (93, -132), (-68, -111), (-80, 2), 
 (6, 45), (-89, 75), (-146, -76), (-94, -24)]

You could also do this using only one list by initializing the resulting list with ranges that you replace with the generated pairs:

pairs = [range(-5,6)]*5 + [range(-100,101)]*75 + [range(-150,151)]*20
for i,R in enumerate(pairs):
    pairs[i] = (0,0)
    while not all(pairs[i]) or pairs.index(pairs[i])&lt;i:
        pairs[i] = random.sample(R,2) # random.choices(R,k=2)

答案2

得分: 2

这是你要翻译的内容:

"已经过了午夜,但我将尽力解释...

基本原则是我要计算在给定范围内(不包括零)有多少不同的配对,然后在该范围上运行一个样本,以确保没有重复项。

然后,我编写一个函数来“解码”这些整数为配对。

所以,如果我想要一个元素在范围-4到+4之间的配对,不包括零,不包括范围-2到+2的配对,我可以将其映射到可能配对的网格...

    -4  -3  -2  -1  +0  +1  +2  +3  +4

+4   O   O   O   O   X   O   O   O   O
+3   O   O   O   O   X   O   O   O   O
+2   O   O   X   X   X   X   X   O   O 
+1   O   O   X   X   X   X   X   O   O 
+0   X   X   X   X   X   X   X   X   X
-1   O   O   X   X   X   X   X   O   O 
-2   O   O   X   X   X   X   X   O   O 
-3   O   O   O   O   X   O   O   O   O
-4   O   O   O   O   X   O   O   O   O

O = 可能的配对
X = 排除的配对

有48种允许的可能配对。

  • 48 = (4² - 2²) * 4

然后,我编写一个函数,可以将值0..47中的每个值转换为这些允许的可能配对之一。

import random
from pprint import pprint

def decode(val, lower, upper):
  q = val % 4
  v = val >> 2
  
  x = v % (upper + lower) - lower
  if x >= 0:
    x += 1
  y = v // (upper + lower) + lower + 1
  
  if q in (1,3):
    x = -x
    y = -y
  
  if q in (2,3):
    x,y = -y,x

  return (x,y)

def pairs(n, lower, upper):
  return [
    decode(id, lower, upper)
      for id in random.sample(
        range((upper**2 - lower**2) * 4),
        n
      )
  ]

# 较小的范围用于使其可打印和可测试
result = [
  *pairs( 5,  0,  5),  # 在范围-5到5的情况下有5对,不包括范围-0到0的情况
  *pairs( 5,  5, 10),  # 在范围-10到10的情况下有5对,不包括范围-5到5的情况 
  *pairs( 5, 10, 20)   # 在范围-20到20的情况下有5对,不包括范围-10到10的情况 
]

pprint(result)

演示:https://trinket.io/python3/f9e65fe9ef"

英文:

It's gone past midnight, but I'll do my best to explain...

The basic principle is that I work out how many different pairs there are in a given range (excluding any zeros), and run a sample on that range, so I know there are no duplicates.

Then I code a function to "decode" those integers in to a pairs.

So, if I want a pair with elements in the range -4 to +4, excluding zeros, excluding pairs from the range -2 to +2, I can map that to a grid of possible pairs...

    -4  -3  -2  -1  +0  +1  +2  +3  +4

+4   O   O   O   O   X   O   O   O   O
+3   O   O   O   O   X   O   O   O   O
+2   O   O   X   X   X   X   X   O   O 
+1   O   O   X   X   X   X   X   O   O 
+0   X   X   X   X   X   X   X   X   X
-1   O   O   X   X   X   X   X   O   O 
-2   O   O   X   X   X   X   X   O   O 
-3   O   O   O   O   X   O   O   O   O
-4   O   O   O   O   X   O   O   O   O

O = Possible pair
X = Excluded pair

There are 48 allowed possible pairs.

  • 48 = (4² - 2²) * 4

I then write a function that can turn each of the values 0..47 in to a different one of those allowed possible pairs.

import random
from pprint import pprint

def decode(val, lower, upper):
  q = val % 4
  v = val &gt;&gt; 2
  
  x = v % (upper + lower) - lower
  if x &gt;= 0:
    x += 1
  y = v // (upper + lower) + lower + 1
  
  if q in (1,3):
    x = -x
    y = -y
  
  if q in (2,3):
    x,y = -y,x

  return (x,y)

def pairs(n, lower, upper):
  return [
    decode(id, lower, upper)
      for id in random.sample(
        range((upper**2 - lower**2) * 4),
        n
      )
  ]

# smaller ranges used to make it printable and testable
result = [
  *pairs( 5,  0,  5),  # 5 pairs with values in the range - 5 to  5, except pairs using the range - 0 to 0
  *pairs( 5,  5, 10),  # 5 pairs with values in the range -10 to 10, except pairs using the range - 5 to 5 
  *pairs( 5, 10, 20)   # 5 pairs with values in the range -20 to 20, except pairs using the range -10 to 10 
]

pprint(result)

Demo : https://trinket.io/python3/f9e65fe9ef

答案3

得分: 1

以下是您要翻译的内容:

我可以这样做使用一个数字列表来获取sample”,而不必多余地删除零使用一个字典来强制唯一性使用集合会更简单但字典会保留插入顺序如果您希望最终列表中的不同样本的排列方式相同并且较小的数字位于前面这很重要):

```python
import random

max_num = 150
nums = list(range(-max_num, max_num))
nums.remove(0)

my_list = list({
    tuple(
        random.sample(nums[max_num-r:r-max_num or None], 2)
    ): 0
    for c, r in ((5, 5), (75, 100), (20, 150))
    for _ in range(c)
})

生成一个单独的nums列表比为每个样本群体生成一个新列表稍微复杂一些,但效率更高,因为我们不需要反复重新分配它,只需根据需要取片段。

请注意,max_num需要至少与for c, r 迭代中所有r值一样大,否则我们将无法通过使用max_num-r等来切片nums来获得所需的范围。


<details>
<summary>英文:</summary>

I might do it like this, using a list of numbers to be able to take the `sample` without having to take an extra step of dropping the zeroes, and using a dict to enforce uniqueness (a set would be simpler, but a dict preserves insertion order, which is important if you want the same arrangement of the different samples in the final list, with smaller numbers toward the beginning):

import random

max_num = 150
nums = list(range(-max_num, max_num))
nums.remove(0)

my_list = list({
tuple(
random.sample(nums[max_num-r:r-max_num or None], 2)
): 0
for c, r in ((5, 5), (75, 100), (20, 150))
for _ in range(c)
})


Generating a single `nums` list is a little more complicated than generating a new list for each sample population, but is more efficient since then we don&#39;t need to repeatedly re-allocate it, and can simply take slices as needed.  

Note that `max_num` needs to be at least as big as all of the `r` values in the `for c, r` iteration, or we won&#39;t be able to get the desired range by slicing `nums` with `max_num-r` etc.

</details>



huangapple
  • 本文由 发表于 2023年3月7日 05:36:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75656074.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定