如何生成随机数,每个随机数与所有其他元素的差至少为x?

时间:2018-11-30 21:32:39

标签: python numpy random

我知道这违反了随机数的定义,但是我的项目仍然需要这样做。 例如,我想在<DataTrigger Binding="{Binding IsSelected}" Value="True"> <DataTrigger.Setters> ... </DataTrigger.Setters> </DataTrigger> 中生成一个包含5个随机元素的数组。

现在,我希望每个元素之间的差异至少为15。 因此,随机数组应如下所示:

range(0, 200)

我可以使用numpy生成随机数:

[15, 45, 99, 132, 199]

但是,我无法保持至少15的一致差异。

6 个答案:

答案 0 :(得分:4)

如果问题显示出更多的心力来解决问题,那就太好了(例如,从Stack Overflow Tour开始:“不要问...您没有尝试寻找答案的问题(请显示您的工作!)”),但有时一个问题会引发瘙痒,您只需要抓挠...

这是一种实现方法,写为函数random_spaced

import numpy as np


def random_spaced(low, high, delta, n, size=None):
    """
    Choose n random values between low and high, with minimum spacing delta.

    If size is None, one sample is returned.
    Set size=m (an integer) to return m samples.

    The values in each sample returned by random_spaced are in increasing
    order.
    """
    empty_space = high - low - (n-1)*delta
    if empty_space < 0:
        raise ValueError("not possible")

    if size is None:
        u = np.random.rand(n)
    else:
        u = np.random.rand(size, n)
    x = empty_space * np.sort(u, axis=-1)
    return low + x + delta * np.arange(n)

例如,

In [27]: random_spaced(0, 200, 15, 5)
Out[27]: array([ 30.3524969 ,  97.4773284 , 140.38221631, 161.9276264 , 189.3404236 ])

In [28]: random_spaced(0, 200, 15, 5)
Out[28]: array([ 81.01616136, 103.11710522, 118.98018499, 141.68196775, 169.02965952])

通过size参数,您可以一次生成多个样本:

In [29]: random_spaced(0, 200, 15, 5, size=3)
Out[29]: 
array([[ 52.62401348,  80.04494534,  96.21983265, 138.68552066, 178.14784825],
       [  7.57714106,  33.05818556,  62.59831316,  81.86507168, 180.30946733],
       [ 24.16367913,  40.37480075,  86.71321297, 148.24263974, 195.89405713]])

此代码使用100000个样本为每个组件生成直方图,并绘制每个组件的相应理论边际PDF:

import matplotlib.pyplot as plt
from scipy.stats import beta

low = 0
high = 200
delta = 15 
n = 5
s = random_spaced(low, high, delta, n, size=100000)

for k in range(s.shape[1]):
    plt.hist(s[:, k], bins=100, density=True, alpha=0.25)
plt.title("Normalized marginal histograms and marginal PDFs")
plt.grid(alpha=0.2)

# Plot the PDFs of the marginal distributions of each component.
# These are beta distributions.
for k in range(n):
    left = low + k*delta
    right = high - (n - k - 1)*delta
    xx = np.linspace(left, right, 400)
    yy = beta.pdf(xx, k + 1, n - k, loc=left, scale=right - left)
    plt.plot(xx, yy, 'k--', linewidth=1, alpha=0.25)
    if n > 1:
        # Mark the mode with a dot.
        mode0 = k/(n-1)
        mode = (right-left)*mode0 + left
        plt.plot(mode, beta.pdf(mode, k + 1, n - k, loc=left, scale=right - left),
                 'k.', alpha=0.25)

plt.show()

这是它生成的图:

plot

从图中可以看出,边际分布为beta distributions。边际分布的模式与n在间隔[low, high]上均匀分布的点的位置相对应。

通过摆弄urandom_spaced的生成方式,可以生成具有不同边际的分布(此答案的旧版本有一个示例),但是random_spaced当前的分布产生似乎是自然的选择。如上所述,边际模式出现在“有意义的”位置。此外,在n为1的情况下,分布简化为[lowhigh]上的均匀分布。

答案 1 :(得分:3)

试错法如何?例如扔一些随机数,排序,计算差异...,如果重复太小?

import random as r

def spreadRandom(theRange, howMany, minSpacing):
    while True:
        candidate = sorted([r.randint(*theRange) for _ in range(howMany)])
        minDiff = min([ candidate[i+1]-candidate[i] for i, _ in enumerate(candidate[:-1])])
        if minDiff >= minSpacing:
            return candidate

spreadRandom([0,200], 5, 15)

您不能保证会得到答案,但是您根本不会像通过基于相邻数字强制执行范围那样完全偏向您的数字。

答案 2 :(得分:2)

尝试将数字0-200改组:

import random
numbers = list(range(200))
random.shuffle(numbers)
distant_numbers = [numbers[0]]
for number in numbers:
    if any(abs(number - x) < 15 for x in distant_numbers):
        continue
    distant_numbers.append(number)
    if len(distant_numbers) >= 5: break

编辑:

以下是使用z3进行大规模杀伤力的解决方案:

def spaced_randoms(n, d, R, first=None):
    solver = z3.SolverFor("QF_FD")
    numbers = [z3.Int("x{}".format(x)) for x in range(n)]
    for number in numbers:
        solver.add(number >= 0)
        solver.add(number <= R)
    for ii in range(n):
        for jj in range(ii+1,n):
            solver.add(z3.Or(numbers[ii] - numbers[jj] > d, numbers[ii] - numbers[jj] < -d))
    if first is not None:
        solver.add(numbers[0] == first)
    result = solver.check()
    if str(result) != "sat":
        raise Exception("Unsatisfiable")
    model = solver.model()
    return [model.get_interp(number) for number in numbers]

像这样调用它以获得随机结果:

import random
spaced_randoms(n, d, R, random.randint(0,R))

答案 3 :(得分:0)

我认为这段代码可能会满足您的特定需求:

import random
import numpy as np
five_list = np.asarray([])
end = False
number = random.randint(0,200)
five_list = np.append(five_list,number)
while True:
    new_number = random.randint(0,200)
    if all(np.absolute(np.subtract(five_list, new_number)) >= 15):
        five_list = np.append(five_list,new_number)
    if np.size(five_list) == 5:
        break
print(np.sort(five_list)) 

答案 4 :(得分:0)

以“蛮力”尝试:

l= [ i for i in range(201) ]
rslt= []
for i in range(5): 
    n=random.choice(l) 
    rslt.append(n) 
    l=[ k for k in l if abs(k-n)>=15 ]
    #if not l:
    #   break

或者明智地:

sgmnts= [(0,200)]
diff= 15
rslt= []

for i in range(5):

    start,stop= sgmnts.pop( random.choice(range(len(sgmnts))) )
    n= random.choice(range(start,stop+1))
    rslt.append(n)
    if n-diff > start:
        sgmnts.append( (start,n-diff) )
    if n+diff < stop:
        sgmnts.append( (n+diff,stop) )
    if not sgmnts:
        break

“ sgmnts”存储适当的范围。我们也根据索引随机选择一个范围。

答案 5 :(得分:-1)

这将在200之间生成5个随机值,步长为5

import random

array = []

randomRange = 200
arrayRange = 5
valueStep = 15

for loop in range(arrayRange):
    randomMaxValue = randomRange - valueStep * (arrayRange - loop) # First loop will the first randomMaxValue be 125 next will be 140, 155, 170, 185, 200
    if not array: # Checks if the array is empty
        array.append(random.randint(0, randomMaxValue)) # Appends a value between 0 and 125 (First will be 125 because 200 - 15 * 5)
    else:
        array.append(random.randint(array[-1] + 15, randomMaxValue)) # Appends the 4 next values

print(array)