python 范围内随机数_Python:在保持最小距离的范围内的随机数字列表

排序案例的单行解决方案

这里有一个简单的一行程序,它以相等的可能性产生所有的可能性:[9*i + x for i, x in enumerate(sorted(random.sample(range(13), 4)))]

一些示例输出:

^{pr2}$

输出总是按排序顺序生成的;如果这不是您想要的,您可以很容易地向结果中添加一个shuffle(或参见下面的一般解决方案)。在

说明:如果[a, b, c, d]是一个满足您要求的有序列表,那么[a, b-9, c-18, d-27]是{}中长度为4的有序样本,反之亦然。因此,您只需从range(13)生成样本,对它们进行排序,然后重新添加9的必要倍数,以获得至少相距10的值。在

一般非排序解

这是一个不需要对随机样本进行排序的通用解决方案。相反,我们计算样本元素的秩,并使用这些元素来计算必要的偏移量。在import random

def ranks(sample):

"""

Return the ranks of each element in an integer sample.

"""

indices = sorted(range(len(sample)), key=lambda i: sample[i])

return sorted(indices, key=lambda i: indices[i])

def sample_with_minimum_distance(n=40, k=4, d=10):

"""

Sample of k elements from range(n), with a minimum distance d.

"""

sample = random.sample(range(n-(k-1)*(d-1)), k)

return [s + (d-1)*r for s, r in zip(sample, ranks(sample))]

以及一些示例输出:>>> sample_with_minimum_distance()

[17, 27, 3, 38]

>>> sample_with_minimum_distance()

[27, 38, 10, 0]

>>> sample_with_minimum_distance()

[36, 13, 1, 24]

>>> sample_with_minimum_distance()

[1, 25, 15, 39]

>>> sample_with_minimum_distance()

[26, 12, 1, 38]

“廉价伎俩”解决方案

如果原始问题中的各种常量是固定不变的(总体range(40),样本长度为4,最小距离为10),那么有一个明显的廉价窍门:只有715可能的不同排序样本,所以只要预先创建一个包含所有这些样本的列表,然后每次需要生成样本,使用random.choice从预先创建的列表中选择一个。在

对于这一代人来说,我们要么采用一种效率极低但显然正确的暴力解决方案:>>> import itertools

>>> all_samples = [ # inefficient brute-force solution

... sample for sample in itertools.product(range(40), repeat=4)

... if all(x - y >= 10 for x, y in zip(sample[1:], sample))

... ]

>>> len(all_samples)

715

这仍然足够快,在我的机器上只需要几秒钟。或者,我们可以使用上面提到的同一个双射来做一些更精细和直接的事情。在>>> all_samples = [

... [9*i + s for i, s in enumerate(sample)]

... for sample in itertools.combinations(range(13), 4)

... ]

>>> len(all_samples)

715

不管怎样,我们只生成一次样本列表,然后在每次需要时使用random.choice来选取一个:>>> random.choice(all_samples)

(1, 11, 21, 38)

>>> random.choice(all_samples)

(0, 10, 23, 33)

当然,这个解决方案不能很好地伸缩:对于最小距离为5的range(100)中的7个样本,可能有超过20亿个不同的排序样本。在

均匀性证明

我在前面说过,一个线性函数以相等的可能性产生所有的可能性(当然,假设一个完美的随机数来源,但是Python的Mersenne Twister足够好,我们不太可能在下面的测试中检测到由核心生成器产生的统计异常)。这就是这种一致性的证明。在

首先,为了方便起见,我们将把一行程序包装在函数中。我们还将其更改为返回tuple,而不是list,因为下一步我们需要一些散列的东西。在>>> def sorted_sample():

... return tuple(9*i + x for i, x in

... enumerate(sorted(random.sample(range(13), 4))))

现在,我们生成1000万个样本(这需要几分钟),并计算每个样本发生的频率:>>> from collections import Counter

>>> samples = Counter(sorted_sample() for _ in range(10**7))

一些快速检查:>>> len(samples)

715

>>> 10**7 / 715

13986.013986013986

>>> samples[0, 10, 20, 30]

14329

>>> samples[0, 11, 22, 33]

13995

>>> min(samples.values())

13624

>>> max(samples.values())

14329

我们收集了715个不同的组合,一点数学知识告诉我们,这正是我们期望的数字(13选4),因此在均匀分布的情况下,我们预计每个组合大约发生10**7 / 715次,或大约14000次。我们上面检查的两个组合都在14000左右,最小和最大计数也出现了,但并不奇怪,有一些随机变化。在

这种随机变化在可接受的范围内吗?{15}我们可以用cd15来做测试。我们的零假设是我们从中提取的总体是一致的:即我们的代码以相等的似然生成每个可能的样本。在>>> from scipy.stats import chisquare

>>> chisquare(list(samples.values()))

Power_divergenceResult(statistic=724.682234, pvalue=0.3825060783237031)

我们得到的p值小于0.01,因此我们无法拒绝无效假设:即,我们没有非均匀性的证据。在

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值