【问题标题】:Random list of ones and zeros with minimum distance between ones1 和 0 之间的最小距离的随机列表
【发布时间】:2017-04-19 13:51:45
【问题描述】:

我想要一个随机列表,其中 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 的列表,但对我来说似乎有点笨拙,所以出于好奇,我想知道是否有更好的方法来做到这一点。

【问题讨论】:

  • 列表中应该总是正好有 100 个,还是应该在总数中有一些小的随机变化?后者可能更“真实”,因为抛一枚公平的硬币 100 次并不总能得到正好 50 个正面,但它可能更难实现。
  • 这是一个测验还是家庭作业,或者您可以为此提供一个实际的用例。无论如何,SO 不是一个代码编写服务,所以至少要付出一些努力来解决这个问题。
  • @Kevin 我编辑了这个问题,是的,应该正好有 100 个。
  • 这是 Python 3.6 还是什么版本?

标签: python list


【解决方案1】:

您基本上有一个二项式随机变量。二项式随机变量成功之间的等待时间由负二项式分布给出。使用此分布,我们可以获得具有指定成功率的二项式变量的成功之间的随机间隔序列。然后我们只需将您的“不应期”添加到所有区间并创建二进制表示。

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()

【讨论】:

  • 需要正好有 10%,而不是机会。所以 1 的数量必须是 == length*0.10
  • 主要编辑。相同的方法,但在分配的时间内不能保证 100 次成功。
  • 我的英语。我的意思是:主要编辑。方法相同,但现在在分配的时间内保证 100 次成功。
【解决方案2】:

我解决这个问题的方法是维护一个候选位置列表,从中随机选择下一个位置。然后,检查周围的位置范围是否为空。如果是这样,则选择该位置并将其周围不允许未来位置的整个范围从可用候选人列表中删除。这确保了最少的循环数。

可能会发生(如果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()

该函数返回一的数组及其个数。集差用于非迭代地去除一系列候选位置。

【讨论】:

    【解决方案3】:

    似乎对于这个问题没有一个非常简单的单行答案。我终于想到了这个:

    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()
    

    这似乎比其他答案更简单,尽管 Paul 的答案在值 n_zeros=900, n_ones=100, min_distance=3 时稍微快了一点

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-24
      • 1970-01-01
      • 2021-03-03
      • 2017-01-02
      • 1970-01-01
      • 2014-02-22
      • 2021-03-19
      • 1970-01-01
      相关资源
      最近更新 更多