对于科学实验,我需要生成一个伪随机顺序,用于管理两个不同的测试,每个测试10次。我已经使用了这段代码:
import random
randy = [1] * 10 + [2] * 10
random.shuffle(randy)
这给了我一个很好的洗牌顺序,但是我需要确保重复测试的最大数量不大于3.换句话说,不要管理" 1"连续测试3次以上。
有人能想到一个好方法吗?多次洗牌并不能保证成功。我能否以任何方式稳健地检查混洗列表并相应地进行更改?谢谢!
答案 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]
它基于表示此自动机中状态的简单函数:
执行规则和一系列选项。如果选项库用完了 - 它会再次尝试。
可能可能需要花费很多时间,但它并没有:
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