0

我有一个从 0 到 149 的 150 个数字的列表。我想使用具有 150 次迭代的 for 循环来生成 150 个 6 个数字的列表,这样,t 在每次迭代k中,k包括数字以及 5 个不同的随机数数字。例如:

S0 = [0, r1, r2, r3, r4, r5] # r1, r2,..., r5 are random numbers between 0 and 150
S1 = [1, r1', r2', r3', r4', r5'] # r1', r2',..., r5' are new random numbers between 0 and 150
...
S149 = [149, r1'', r2'', r3'', r4'', r5''] 

此外,每个列表中的数字必须不同,并且最小距离为 5。这是我正在使用的代码:

import random
import numpy as np

final_list = []
for k in range(150):
    S = [k]
    for it in range(5):
        domain = [ele for ele in range(150) if ele not in S]
        d = 0
        x = k
        while d < 5:
            d = np.Infinity
            x = random.sample(domain, 1)[0]
            for ch in S:
                if np.abs(ch - x) < d:
                    d = np.abs(ch - x)
        S.append(x)
    final_list.append(S)

输出:

[[0, 149, 32, 52, 39, 126],
 [1, 63, 16, 50, 141, 79],
 [2, 62, 21, 42, 35, 71],
...
 [147, 73, 38, 115, 82, 47],
 [148, 5, 78, 115, 140, 43],
 [149, 36, 3, 15, 99, 23]]

现在,代码正在运行,但我想知道是否可以强制每个数字在所有迭代中的重复次数大致相同。例如,使用前面的代码后,此图指示每个数字在生成的列表中出现的次数:

代表

如您所见,有些数字出现了 10 次以上,而有些数字只出现了 2 次。是否可以减少这种变化水平,以便该图可以近似为均匀分布?谢谢。

4

2 回答 2

1

首先,我不确定您关于当前结果不是均匀分布的断言是否一定正确。对我来说,尝试检查多次重复该过程的直方图似乎是谨慎的,而不仅仅是一次。

我不是统计学家,但是当我想近似均匀分布(并假设函数random提供均匀分布)时,我尝试做的是简单地接受random函数返回的所有结果。为此,我需要在调用这些函数之前限制它们的选择。这就是我将如何处理您的任务:

import random
import numpy as np

N = 150

def random_subset(n):
    result = []
    cands = set(range(N))
    for i in range(6):
        result.append(n)                  # Initially, n is the number that must appear in the result
        cands -= set(range(n - 4, n + 5)) # Remove candidates less than 5 away 
        n = random.choice(list(cands))    # Select next number
    return result

result = np.array([random_subset(n) for n in range(N)])
print(result)

简单地说,每当我n在结果集中添加一个数字时,我都会从选择候选中取出一个适当大小的环境,以确保将来不会选择距离小于 5 的数字。

该代码未优化(多次set转换list),但它可以工作(根据我的理解)。

于 2020-06-13T09:52:26.503 回答
0

如果你愿意,你可以强制它完全一致。

为全局变量和本地变量的混合道歉,这似乎是最易读的。您可能希望根据常量的可变性进行重写 =)

import random

SIZE = 150
SAMPLES = 5

def get_samples():
    pool = list(range(SIZE)) * SAMPLES
    random.shuffle(pool)
    items = []
    for i in range(SIZE):
        selection, pool = pool[:SAMPLES], pool[SAMPLES:]
        item = [i] + selection
        items.append(item)
    return items

然后你将有每个正好 5 个(还有一个处于领先位置,这是一个奇怪的数据结构)。

>>> set(collections.Counter(vv for v in get_samples() for vv in v).values())                                                                      
{6}

上面的方法不保证最后 5 个数字是唯一的,事实上,你会期望 ~10/150 有重复。如果这很重要,您需要对您的分布进行更多过滤,并确定您对紧密一致性、重复等的重视程度。

如果您的数字与您在上面给出的大致相同,您还可以(公平地)修补结果并希望避免长时间的搜索(SAMPLES尺寸更接近OPTIONS尺寸的情况并非如此)

def get_samples():
    pool = list(range(SIZE)) * SAMPLES
    random.shuffle(pool)
    i = 0
    while i < len(pool):
        if i % SAMPLES == 0:
            seen = set()
        v = pool[i]
        if v in seen:  # swap
            dst = random.choice(range(SIZE))
            pool[dst], pool[i] = pool[i], pool[dst]
            i = dst - dst % SAMPLES  # Restart from swapped segment
        else:
            seen.add(v)
            i += 1
    items = []
    for i in range(SIZE):
        selection, pool = pool[:SAMPLES], pool[SAMPLES:]
        assert len(set(selection)) == SAMPLES, selection
        item = [i] + selection
        items.append(item)
    return items

这通常需要不到 5 次通过来清理任何重复项,并且应该使所有安排同样可能满足您的条件。

于 2020-06-13T22:36:47.167 回答