带有重复约束的shuffle列表2-后台python



我有以下列表:

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]
  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

这里有一个满足约束的解决方案,并且总是为每个元素返回一个列表两次。它在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

最新更新