[英]How to generate random numbers with each random number having a difference of at least x with all other elements?
I know this goes against the definition of random numbers, but still I require this for my project. 我知道这违反了随机数的定义,但我仍然需要这个项目。 For instance, I want to generate an array with 5 random elements in
range(0, 200)
. 例如,我想生成一个在
range(0, 200)
有5个随机元素的数组。
Now, I want each of the elements to have a difference of at least 15 between them. 现在,我希望每个元素之间的差异至少为15。 So the random array should look something like this:
所以随机数组看起来应该是这样的:
[15, 45, 99, 132, 199]
I can generate random numbers using numpy: 我可以使用numpy生成随机数:
np.random.uniform(low=0, high=200, size=5)
However, I am not able to keep a consistent difference of at least 15. 但是,我无法保持至少15的一致差异。
It would be nice if the question showed more effort towards solving the problem (ie from the Stack Overflow Tour : "Don't ask about... Questions you haven't tried to find an answer for (show your work!)"), but sometimes a question triggers an itch you just have to scratch... 如果问题显示出更多努力来解决问题(例如来自Stack Overflow Tour :“不要问...问题,你没有试图找到答案(显示你的工作!)”,这将是很好的。) ,但有时候一个问题会引发痒,你只需要刮开......
Here's one way you could do it, written as the function random_spaced
: 这是你可以做到的一种方式,写成函数
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)
For example, 例如,
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])
The size
argument lets you generate more than one sample at a time: 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]])
This code generates a histogram for each component using 100000 samples, and plots the corresponding theoretical marginal PDFs of each component: 此代码使用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()
Here's the plot that it generates: 这是它生成的情节:
As can be seen in the plot, the marginal distributions are beta distributions . 从图中可以看出,边际分布是β分布 。 The modes of the marginal distributions correspond to the positions of
n
evenly spaced points on the interval [low, high]
. 边际分布的模式对应于区间
[low, high]
上的n
均匀间隔点的位置。
By fiddling with how u
is generated in random_spaced
, distributions with different marginals can be generated (an old version of this answer had an example), but the distribution that random_spaced
currently generates seems to be a natural choice. 通过摆弄在
random_spaced
生成u
方式,可以生成具有不同边缘的分布(这个答案的旧版本有一个例子),但random_spaced
当前生成的分布似乎是一个自然的选择。 As mentioned above, the modes of the marginals occur in "meaningful" positions. 如上所述,边缘的模式出现在“有意义”的位置。 Moreover, in the trivial case where
n
is 1, the distribution simplifies to the uniform distribution on [ low
, high
]. 此外,在
n
为1的平凡情况下,分布简化为[ low
, high
]上的均匀分布。
How about trial-and-error? 反复试验怎么样? eg throw some random numbers, sort, compute differences... and if too small repeat?
例如,抛出一些随机数,排序,计算差异......如果重复得太小?
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)
You're not guaranteed to ever get an answer, but you're not biasing your numbers at all as you might be by enforcing ranges based on neighboring numbers. 您无法保证得到答案,但您根本不会偏离您的数字,因为您可能会强制执行基于相邻数字的范围。
Try shuffling the numbers 0-200: 尝试改组数字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
Edit: 编辑:
Here's a solution that uses z3
for massive overkill: 这是一个使用
z3
进行大规模过度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 it like this for a random result: 像这样称它为随机结果:
import random
spaced_randoms(n, d, R, random.randint(0,R))
I think this code might help for your specific needs: 我认为此代码可能有助于满足您的特定需求:
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))
Try it with "brute force": 尝试“蛮力”:
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
Or smartly: 或巧妙地:
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" stores the suitable ranges. “sgmnts”存储合适的范围。 We select a range randomly, too, by index.
我们也通过索引随机选择一个范围。
This will generate 5 random values between 200 with a step of 5 这将在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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.