限制伪随机python列表中的重复次数

时间:2015-03-16 15:52:23

标签: python random repeat shuffle

对于科学实验,我需要生成一个伪随机顺序,用于管理两个不同的测试,每个测试10次。我已经使用了这段代码:

import random
randy  = [1] * 10 + [2] * 10 
random.shuffle(randy)

这给了我一个很好的洗牌顺序,但是我需要确保重复测试的最大数量不大于3.换句话说,不要管理" 1"连续测试3次以上。

有人能想到一个好方法吗?多次洗牌并不能保证成功。我能否以任何方式稳健地检查混洗列表并相应地进行更改?谢谢!

6 个答案:

答案 0 :(得分:2)

这是一种乐观重试策略:

#!/usr/bin/env python
from random import choice

def added1(lst, bank):
    if len(bank) == 0:
        return lst
    selection = choice(bank)
    lst.append(selection)
    bank.remove(selection)
    if selection == 1:
        return added11(lst, bank)
    return added2(lst, bank)

def added11(lst,bank):
    if len(bank) == 0:
        return lst
    bank.remove(2)
    lst.append(2)
    return added2(lst, bank)

def added2(lst, bank):
    if len(bank) == 0:
        return lst
    selection = choice(bank)
    lst.append(selection)
    bank.remove(selection)
    if selection == 2:
        return added22(lst, bank)
    return added1(lst, bank)

def added22(lst,bank):
    if len(bank) == 0:
        return lst
    bank.remove(1)
    lst.append(1)
    return added1(lst, bank)

def start(lst, bank):
    bank_bkp = bank[:]
    while True:
        try:
            if len(bank) == 0:
                return lst
            selection = choice(bank)
            lst.append(selection)
            bank.remove(selection)
            if selection == 1:
                return added1(lst, bank)
            return added2(lst, bank)
        except:
            # retry
            bank = bank_bkp[:]
            lst = []


print start([], [1] * 10 + [2] * 10)

输出:

  

[1,1,2,1,1,2,2,1,2,1,1,2,2,1,1,2,2,1,2,2]

它基于表示此自动机中状态的简单函数: automaton

执行规则和一系列选项。如果选项库用完了 - 它会再次尝试。

可能可能需要花费很多时间,但它并没有:

print timeit.repeat('start([], [1] * 10 + [2] * 10)', setup="from __main__ import start", number=10000, repeat=3)

输出:

  

[0.14524006843566895,0.14585399627685547,0.14375996589660645]

注意:这是递归的,因此拥有超过2000名成员的银行要求您明确允许更深入的递归。

答案 1 :(得分:2)

对于如此小的问题,我不同意@ texasflood的评论,即预先计算所有可能性,然后从中挑选它们将是非常低效的。实际上,所述参数非常小,以至于只使用纯粹的强力来产生所有可能性是非常容易管理的,正如我将在下面演示的那样。

在您的特定情况下,您始终只运行20次测试,并且您只有2种可能的测试可供选择。所以你知道你不可能有超过2 ** 20个序列,即使没有其他限制。这只有1048576种可能性,可以通过今天的记忆轻松管理。

此外,根据您的问题陈述,您仅限于使用10个测试和10个其他测试。这减少了184756的可能性。(使用经典概率计数技术,计算结果为20!/(10!* 10!)。)

在此之前,您甚至已经连续四次(或更多次)同一测试的运行消除了这种可能性。

所以,我的强烈建议是完成计算所有可用可能性的工作,然后在这组可能性上使用random.choice

为了帮助您入门,这是一个简单的循环,可以获得包含10个零和10个零的所有可能序列:

sequences = []
for n in range(2**20):
    b = bin(n)[2:].zfill(20)
    if b.count('1') == 10:
        sequences.append(b)

请注意,bin函数(需要Python 2.6或更高版本)生成一个整数的二进制字符串表示形式,以'0b'开头(因此[2:]将其剥离)

我将其作为练习让读者消除四行连续序列。 (提示:您可以通过测试二进制字符串中'1111'的存在或'0000'的存在来简化上面给出的示例代码。最后总共有66486个可用序列,根据今天的标准,这个数字相当少。)

答案 2 :(得分:2)

John Y的解决方案让您搜索整个解决方案空间;虽然这是可以忍受的,但它几乎不值得做。相反,只需乐观地采样:

import random

sequences = []
order = [1, 0] * 10

while len(sequences) < 10:
    random.shuffle(order)

    if order in sequences:
        continue

    sequences.append(order[:])

然后要删除长度为4的组,您可以查看

之类的内容
from itertools import groupby

while len(sequences) < 10:
    random.shuffle(order)

    if order in sequences:
        continue

    if all(len(list(group)) < 4 for _, group in groupby(order)):
        sequences.append(order[:])

答案 3 :(得分:0)

我会制作你自己的洗牌器,因为它可能是最快和最优雅的选择:

randy = []

ones = [1] * 10
twos = [2] * 10

for i in range(20):
    if len(randy) > 3 and randy[i-1] == randy[i-2] == randy[i-3]:
        randy.append(ones.pop() if randy[i-1] == 1 else twos.pop())
    else:
        randy.append(random.choice([ones, twos]).pop())

答案 4 :(得分:0)

这并不完全保留10 1和10 2,所以可能不是你所追求的,但它取决于机会(目前每个50%),你可以根据需要添加新的测试。

import random
from operator import itemgetter

#randy = [ [item,amount], ... ]
randy  = [[1,10],[2,10]]

#This turns the above list into the same format of your 'randy'
itemList = [j for k in[([i[0]]*i[1])for i in randy]for j in k]  

randomList = [-1]  #This stops the check from causing problems at the start
for i in range(len(itemList)):
    while True:
        newChoice = random.choice( itemList )
        if len(set(randomList[-2:]+[newChoice]))-1: #Checks the last 2 values plus the new value aren't all the same
            randomList.append( newChoice )
            break
shuffledList = randomList[1:]

答案 5 :(得分:0)

这里是Veedrac答案的优化版本,您只想在其中得到一个正确的列表。如果想要即时获得序列 会更有趣,但是如果想要避免序列重复则更有趣。

from random import shuffle
from itertools import groupby

def get_binary_sequence(sequence_length, maximum_repetitions):
    order = [True, False] * int(sequence_length/2)
    while True:
        shuffle(order)
        if all(len(list(group)) <= maximum_repetitions _, group in groupby(order)):
            return order