我想有一个随机列表,其中1的出现是10%,其余的项是零。这个列表的长度是1000.我希望值以随机顺序排列,以便在它们之间存在可调整的最小距离。因此,例如,如果我选择值3,列表将如下所示:
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, ...]
实现这一目标最优雅的方法是什么?
编辑。我被要求提供更多信息并付出一些努力。
这是一项研究,其中0表示一种类型的刺激,1表示另一种刺激,我们希望刺激类型1之间的距离最小。
到目前为止,我已经实现了这个目标:
trials = [0]*400
trials.extend([1]*100)
random.shuffle(trials)
#Make sure a fixed minimum number of standard runs follow each deviant
i = 0
while i < len(trials):
if trials[i] == 1:
trials[i+1:i+1] = 5*[0]
i = i + 6
else:
i = i + 1
这给了我一个1000长度的列表,但对我来说似乎有点笨拙,出于好奇,我想知道是否有更好的方法来做到这一点。
答案 0 :(得分:0)
你基本上有一个二项式随机变量。二项式随机变量的成功之间的等待时间由负二项分布给出。使用此分布,我们可以获得具有指定成功率的二项式变量的成功之间的随机间隔序列。然后我们简单地将“不应期”添加到所有区间并创建二进制表示。
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import nbinom
min_failures = 3 # refractory period
total_successes = 100
total_time = 1000
# create a negative binomial distribution to model the waiting times to the next success for a Bernoulli RV;
rv = nbinom(1, total_successes / float(total_time))
# get interval lengths between successes;
intervals = rv.rvs(size=total_successes)
# get event times
events = np.cumsum(intervals)
# rescale event times to fit into the total time - refractory time
total_refractory = total_successes * min_failures
remaining_time = total_time - total_refractory
events = events.astype(np.float) / np.max(events) * remaining_time
# add refractory periods
intervals = np.diff(np.r_[0, events])
intervals += min_failures
events = np.r_[0, np.cumsum(intervals[:-1])] # series starts with success
# create binary representation
binary = np.zeros((total_time), dtype=np.uint8)
binary[events.astype(np.int)] = 1
要检查事件间间隔是否符合您的期望,请绘制直方图:
# check that intervals match our expectations
fig, ax = plt.subplots(1,1)
ax.hist(intervals, bins=20, normed=True);
ax.set_xlabel('Interval length')
ax.set_ylabel('Normalised frequency')
xticks = ax.get_xticks()
ax.set_xticks(np.r_[xticks, min_failures])
plt.show()
答案 1 :(得分:0)
我解决这个问题的方法是维护一个候选位置列表,从中随机选择下一个位置。然后,检查周围的位置范围是否为空。如果是,则选择该位置,并且从可用候选者列表中移除其周围不允许未来位置的整个范围。这确保了最少的循环次数。
可能会发生(如果mindist
与位置数相比较大),小于所需位置的返回。在这种情况下,需要再次调用该函数,如图所示。
import random
def array_ones(mindist, length_array, numones):
result = [0]*length_array
candidates = range(length_array)
while sum(result) < numones and len(candidates) > 0:
# choose one position randomly from candidates
pos = candidates[random.randint(0, len(candidates)-1)]
L = pos-mindist if pos >= mindist else 0
U = pos+mindist if pos <= length_array-1-mindist else length_array-1
if sum(result[L:U+1]) == 0: # no taken positions around
result[pos] = 1
# remove all candidates around this position
no_candidates = set(range(L, U+1))
candidates = list(set(candidates).difference(no_candidates))
return result, sum(result)
def main():
numones = 5
numtests = 50
mindist = 4
while True:
arr, ones = array_ones(mindist, numtests, numones)
if ones == numones:
break
print arr
if __name__ == '__main__':
main()
该函数返回1的数组及其数目。设置差异用于非迭代地移除一系列候选位置。
答案 2 :(得分:0)
似乎对这个问题没有一个非常简单的单行答案。我终于想到了这个:
import numpy as np
def construct_list(n_zeros, n_ones, min_distance):
if min_distance > (n_zeros + n_ones) / n_ones:
raise ValueError("Minimum distance too high.")
initial_zeros = n_zeros - min_distance * n_ones
block = np.random.permutation(np.array([0]*initial_zeros + [1]*n_ones))
ones = np.where(block == 1)[0].repeat(min_distance)
#Insert min_distance number of 0s after each 1
block = np.insert(block, ones+1, 0)
return block.tolist()
这似乎比其他答案更简单,尽管保罗的答案只是稍快一点,值n_zeros = 900,n_ones = 100,min_distance = 3