Как сгенерировать случайные числа, в которых каждое случайное число имеет разность не менее x со всеми другими элементами? - PullRequest
0 голосов
/ 01 декабря 2018

Я знаю, что это идет вразрез с определением случайных чисел, но все же мне требуется это для моего проекта.Например, я хочу сгенерировать массив из 5 случайных элементов в range(0, 200).

Теперь я хочу, чтобы каждый из элементов имел разницу не менее 15 между ними.Таким образом, случайный массив должен выглядеть примерно так:

[15, 45, 99, 132, 199]

Я могу генерировать случайные числа, используя numpy:

np.random.uniform(low=0, high=200, size=5)

Однако я не могу сохранить постоянную разницу по крайней мере15.

Ответы [ 6 ]

0 голосов
/ 01 декабря 2018

Было бы неплохо, если бы вопрос показывал больше усилий для решения проблемы (например, из Stack Overflow Tour : «Не задавайте вопросы ... Вопросы, которые вы не пытались найти ответ»)for (покажите свою работу!) "), но иногда вопрос вызывает зуд, который вам просто нужно поцарапать ...

Вот один из способов, которым вы могли бы это сделать, записанный в виде функции 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

Как видно на графике, маргинальные распределения бета-распределения .Режимы маржинальных распределений соответствуют позициям n равномерно распределенных точек на интервале [low, high].

Путем использования способа генерации u в random_spaced можно получить распределения с разными маргиналамиГенерируемый (в старой версии этого ответа был пример), но дистрибутив, который генерирует random_spaced, кажется естественным выбором.Как упоминалось выше, режимы маргиналов встречаются в «значимых» позициях.Более того, в тривиальном случае, когда n равен 1, распределение упрощается до равномерного распределения на [low, high].

0 голосов
/ 01 декабря 2018

Попробуйте «грубой силой»:

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» сохраняет подходящие диапазоны.Мы также выбираем диапазон случайным образом по индексу.

0 голосов
/ 01 декабря 2018

Я думаю, что этот код может помочь для ваших конкретных потребностей:

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)) 
0 голосов
/ 01 декабря 2018

Как насчет проб и ошибок?например, выбрасывать случайные числа, сортировать, вычислять различия ... и, если слишком мало, повторить?

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)

Вам не гарантировано, что вы когда-либо получите ответ, но вы не будете смещать свои числа какВы могли бы, применяя диапазоны, основанные на соседних числах.

0 голосов
/ 01 декабря 2018

Это сгенерирует 5 случайных значений между 200 с шагом 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)
0 голосов
/ 01 декабря 2018

Попробуйте перетасовать цифры 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]

Callэто случайный результат:

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