带重复约束的随机列表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]
  • 重新洗牌if
    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。当它们相遇时,您将有一个大部分被洗牌的列表,并且在末尾有一些流氓值。然后找到适当的位置插入这些值。存在大量流氓值的几率