带重复约束的随机列表2-back python
我有以下清单:带重复约束的随机列表2-back python,python,shuffle,Python,Shuffle,我有以下清单: a=['airplane','track','car','train'] 我想创建一个列表,该列表中的每一项都显示两次,但不允许在接下来的两行中重复项目。这意味着飞机只能出现在飞机之后,只要两个不同的项目在两者之间,因此b将是一个很好的候选者: b=['airplane','track','car','airplane','train','track','car' etc.] 但c不会: c=['airplane,'track','airplane', etc.] 我在考虑
a=['airplane','track','car','train']
我想创建一个列表,该列表中的每一项都显示两次,但不允许在接下来的两行中重复项目。这意味着飞机只能出现在飞机之后,只要两个不同的项目在两者之间,因此b将是一个很好的候选者:
b=['airplane','track','car','airplane','train','track','car' etc.]
但c不会:
c=['airplane,'track','airplane', etc.]
我在考虑某种暴力行动,其中:
1.a是重复的
2.随机。洗牌(a)
3.重复测试(可能类似于以下内容:
curWord[n]==curWord[n+1]
TRUE
,然后重新开始。(我不知道指示python读取真值的命令是什么。我想象一个if
语句会起作用,但是在FALSE
的情况下,我不知道如何指示python继续执行。)提前感谢您的帮助!如果您只需要一个列表,其中每个元素有两个副本,那么当原始列表长度超过2个元素时,有什么原因不能使用该列表
In [138]: a=['airplane','track','car','train']
In [139]: a + a
Out[139]: ['airplane', 'track', 'car', 'train', 'airplane', 'track', 'car', 'train']
如果您要问一个更抽象的问题,“如何从列表元素的排列空间中采样,使它们不会出现在同一元素的两个元素中”,那么下面的问题应该可以解决
请注意,获取某些元素出现两次的结构与a+a
一样简单,然后您可以担心限制a+a
的排列——无需过度思考问题的“如何获得每个元素中的两个”部分
import random
def valid_duplicate_spacing(x):
for i, elem in enumerate(x):
if elem in x[i+1:i+3]:
return False
return True
def sample_permutations_with_duplicate_spacing(seq):
sample_seq = seq + seq
random.shuffle(sample_seq)
while not valid_duplicate_spacing(sample_seq):
random.shuffle(sample_seq)
return sample_seq
然后可按如下方式使用:
In [165]: sample_permutations_with_duplicate_spacing(a)
Out[165]: ['airplane', 'train', 'track', 'car', 'train', 'track', 'car', 'airplane']
In [166]: sample_permutations_with_duplicate_spacing(a)
Out[166]: ['train', 'airplane', 'car', 'track', 'train', 'airplane', 'track', 'car']
如果您所说的只是从列表中随机抽样,这样就不会为以下两次抽取替换一个样本,那么您可以使用生成器:
import random
def draw_with_delayed_replacement(seq):
drawn = random.choice(seq)
rejectables = [drawn]
yield drawn
drawn = random.choice(seq)
while drawn in rejectables:
drawn = random.choice(seq)
rejectables.append(drawn)
yield drawn
while True:
drawn = random.choice(seq)
if drawn in rejectables:
continue
else:
rejectables.pop(0)
rejectables.append(drawn)
yield drawn
然后您可以执行以下操作:
In [146]: foo = draw_with_delayed_replacement(a)
In [147]: foo.next()
Out[147]: 'car'
In [148]: foo.next()
Out[148]: 'train'
In [149]: foo.next()
Out[149]: 'track'
In [150]: foo.next()
Out[150]: 'car'
In [151]: foo.next()
Out[151]: 'train'
In [152]: foo.next()
Out[152]: 'track'
In [153]: foo.next()
Out[153]: 'car'
In [154]: foo.next()
Out[154]: 'airplane'
In [155]: foo.next()
Out[155]: 'track'
In [156]: foo.next()
Out[156]: 'train'
但是,在这种情况下,您不能保证每个元素都会出现两次,这对于小列表来说可能效率低下。我的解决方案是,它不能保证每个标记都出现n次 我们可以很容易地扩展我的解决方案来保证这一点,但这将导致可能出现的死锁场景,我们必须在那时进行检查
>>> def magicsequence(tokens, length):
... sequence = []
... while len(sequence) < length:
... candidate = random.choice(tokens)
... if candidate not in sequence[-2:]:
... sequence.append(candidate)
... return sequence
def magicsequence(标记、长度):
…序列=[]
…而len(序列)<长度:
…候选=随机。选择(令牌)
…如果候选项不按顺序[-2:]:
…序列。追加(候选)
…返回序列
这是一个满足约束条件的解决方案,它总是返回一个包含每个元素的列表两次。它在O(n^2)时间内运行,其中n是列表的长度
from collections import Counter
from itertools import permutations
from random import choice, shuffle
def shuffle_with_constraints(x):
if len(x) < 3:
raise ValueError("Lists with length less than 3 have no valid shuffles")
output = []
#a counter representing how many times we still need to insert an element
available = Counter(x*2)
while available:
if len(output) == len(x)*2-6: #we just need to insert six elements
distinct = len(available) #how many distinct elements we need to insert
if distinct == 6:
pass #there is no problem
elif distinct == 3: #only six possibilities
end = list(available)
shuffle(end)
return output + end*2
else:
#enumerate all 720 permutations and select a valid one
possibles = [possible for possible in permutations(available.elements())
if valid_6_shuffle(possible)]
return output+list(choice(possibles))
#choose a valid element and append it to the output
next = choice(list(set(available)-set(output[-2:])))
output.append(next)
#remove it from the availables
if available[next] == 2:
available[next] -= 1
else:
del available[next]
return output
def valid_6_shuffle(x):
for a,b,c in zip(x,x[1:],x[2:]):
if a==b or b==c or a==c:
return False
return True
从集合导入计数器
从itertools导入置换
从随机导入选择,洗牌
def shuffle_与_约束(x):
如果len(x)<3:
raise VALUERROR(“长度小于3的列表没有有效的洗牌”)
输出=[]
#一个计数器,表示仍需要插入元素的次数
可用=计数器(x*2)
可用时:
如果len(output)==len(x)*2-6:#我们只需要插入六个元素
distinct=len(可用)#需要插入多少个不同的元素
如果distinct==6:
通过,没问题
elif distinct==3:#只有六种可能性
结束=列表(可用)
洗牌(完)
返回输出+结束*2
其他:
#枚举所有720个排列并选择一个有效排列
possibles=[可能用于置换中的可能(available.elements())
如果有效_6_shuffle(可能)]
返回输出+列表(选项(可能))
#选择有效元素并将其附加到输出
下一步=选择(列表(集合(可用)-集合(输出[-2:]))
output.append(下一步)
#将其从可用组件中移除
如果可用[下一步]==2:
可用[下一个]-=1
其他:
del可用[下一步]
返回输出
def有效_6_随机播放(x):
对于zip中的a、b、c(x,x[1:],x[2:]):
如果a==b或b==c或a==c:
返回错误
返回真值
如果设置foo=tokens+tokens
,然后从foo
中绘制candidate
,只要在If
语句的append
步骤中成功,就会从foo
中删除一个candidate
副本,那么这将保证每个元素出现两次属性。但是,在这一点上,它将是一个比使用random.shuffle
效率更低的实现,因为重复调用random.choice
。是的,并且可能会出现死锁:当只有[“飞机”,“飞机”]例如,左。@ch3ka也感谢您的建议。非常有用!特别是考虑到您与EMS的讨论。非常感谢。解决方案1正是我需要做的。顺便说一下,非常好的解释。这一切都很有意义。解决方案2很有趣!可以用O(n)编写一个解决方案时间。@senderle你介意解释一下如何在O(n)时间内完成吗?嗯,我很确定你可以。我写了一个版本,可以使用原始设置(每个项目有两个副本,间隔为2)但我无法让它使用更高的值,因此实际上出现了一些问题。我没有时间调试,所以没有发布它。其想法是,您使用一个修改的,它还跟踪列表的结尾。当您选择“非法”时值,将其移动到列表的末尾,并将指向末尾的指针向下移动1。当它们相遇时,您将有一个大部分被洗牌的列表,并且在末尾有一些流氓值。然后找到适当的位置插入这些值。存在大量流氓值的几率