如何确保 np.random.choice 中的一对项目不会一起出现在一个循环中?

How to make sure a pair of items in np.random.choice don't appear together in a loop?

我目前正在尝试 运行 蒙特卡洛模拟而不用替换,使用 np.random.choice,但是列表中的某些项目不能一起出现。例如,如果我有一个包含五个项目的列表:RO、MI、VE、NA、SI,并且每个循环产生一组四个项目,RO 可以与 VE、MI 或 NA 一起出现,但不能与 SI 一起出现,在每次迭代中。 所以像这样的循环:[RO,MI,VE,NA] 是正确的 但不是:[RO,MI,SI,NA],因为 SI 出现在与 RO 相同的组中。 这是我目前使用的代码(产生了不正确的分组):

np.random.seed(42)
portfolio=[]
for i in range(0,10000):
    port_gar = list(np.random.choice(cities, size=4, replace=False, p=cities_prob))
    portfolio.append(port_gar)
print(portfolio)

我不知道该怎么做,我们将不胜感激。谢谢!

在不了解更多关于哪些城市可以出现在列表中的限制的情况下,最简单的解决方案可能是随机生成城市,直到您拥有足够多的有效城市。例如:

def check_valid(port_gar):
    is_valid = True
    
    if 'RO' in port_gar:
        is_valid = is_valid and ('NA' not in port_gar and 'VE' not in port_gar)
    if 'NA' in port_gar:
        is_valid = is_valid and 'VE' not in port_gar
    
    # Add other checks for constraints here

    return is_valid

portfolio = []
while len(portfolio) < 10000:
    port_gar = list(np.random.choice(cities, size=4, replace=False, p=cities_prob))
    if check_valid(port_gar):
        portfolio.append(port_gar)

print(portfolio)

这绝对不是最高效的实现方式,但如果样本数量相对较少(本例中为 10000)且城市数量较多,那么性能应该没问题。

我提出了两个解决方案,它们都使用了标准库中的 random 模块。我推荐这种方法而不是使用 numpy,因为你不能以任何方式在这里真正利用 numpy,特别是因为你甚至没有在最后得到一个 numpy 数组(你只是 使用 numpy 生成随机数)。

解决方案 1:每个样本都有恰好一个城市来自“独家城市”组

您可以做的是将表现不佳的城市与其他城市分开:

import random
random.seed(42)


n = 10  # whatever total number of items you want per sample
num_samples = 10000  # total number of samples


free_cities = [...]  # all cities in this group play nicely together
exclusive_cities = [...]  # cities in this group cannot be grouped in a sample, so we will only ever choose one city at a time from this group


portfolio = []
for _ in range(num_samples):
    free_sample = random.sample(free_cities, k=(n-1))
    exclusive_sample = random.sample(exclusive_cities, k=1)
    portfolio.append(random.sample(free_sample + exclusive_sample, k=n))

这是一个人为的例子:

n = 6
num_samples = 10

free_cities = ["AW", "JB", "EX", "HZ", "MZ", "XQ", "KA", "MW", "WZ", "UD"]
exclusive_cities = ["RO", "VE", "SI", "NA"]


portfolio = []
for _ in range(num_samples):
    free_sample = random.sample(free_cities, k=(n-1))
    exclusive_sample = random.sample(exclusive_cities, k=1)
    portfolio.append(random.sample(free_sample + exclusive_sample, k=n))

输出:

>>> portfolio
[['AW', 'EX', 'WZ', 'RO', 'MZ', 'JB'],
 ['RO', 'MZ', 'KA', 'WZ', 'XQ', 'EX'],
 ['AW', 'MW', 'VE', 'JB', 'WZ', 'XQ'],
 ['NA', 'WZ', 'JB', 'MW', 'EX', 'MZ'],
 ['SI', 'UD', 'AW', 'JB', 'WZ', 'MW'],
 ['MZ', 'JB', 'UD', 'VE', 'WZ', 'HZ'],
 ['NA', 'KA', 'UD', 'AW', 'MW', 'WZ'],
 ['JB', 'NA', 'UD', 'KA', 'WZ', 'MW'],
 ['MZ', 'RO', 'JB', 'HZ', 'AW', 'XQ'],
 ['WZ', 'HZ', 'UD', 'JB', 'VE', 'XQ']]

现在需要注意的是,这将保证每个样本都只有 exclusive_cities 组中的一个城市:

for sample in portfolio:
    print(set(sample) & set(exclusive_cities))

输出:

# Every sample has one item from the exclusive_cities group
{'RO'}
{'RO'}
{'VE'}
{'NA'}
{'SI'}
{'VE'}
{'NA'}
{'NA'}
{'RO'}
{'VE'}

解决方案 2:每个样本最多 一个 个来自“独家城市”组的城市

如果这不是您想要的行为,您可以使用额外的“掷硬币”来确定给定示例中是否出现 1 或 0 个排他性城市,并对上述逻辑稍作修改:

n = 6
num_samples = 10


portfolio = []
for _ in range(num_samples):
    include = random.choice((0, 1))
    free_sample = random.sample(free_cities, k=(n - include))
    exclusive_sample = random.sample(exclusive_cities, k=include)
    portfolio.append(random.sample(free_sample + exclusive_sample, k=n))

现在您的样本可能包含也可能不包含一个专属城市,但仍然只有一个最大值:

# An empty set means an exclusive city isn't present in that sample
set()
set()
{'SI'}
{'NA'}
set()
{'RO'}
{'SI'}
{'VE'}
set()
{'RO'}