【问题标题】:Enforcing "no 2 same contiguous elements" in random list generation在随机列表生成中强制执行“没有 2 个相同的连续元素”
【发布时间】:2011-07-11 07:49:35
【问题描述】:

我有一组 4 个字符串,并且想要生成一个包含 16 个元素的列表,但是通过执行规则(或获得与执行此类规则相同的结果)永远不会在结果中的两个连续位置重复相同的元素列表。

几乎是 Python 的新手,我去检查了随机库中的不同方法,发现了许多不同且有用的方法来做类似的事情(random.shuffle 几乎可以解决问题),但没有一个解决这个问题我的特殊需要。

我应该使用什么数据格式和方法?

【问题讨论】:

    标签: python algorithm list random rules


    【解决方案1】:

    伪代码算法:

    1. For i in n(n 是您想要的元素数量)
    2. 生成下一个元素
    3. 如果和前一个元素相同,重复2

    使用random.choice 从元素列表中随机选择一个元素。

    这是一个概念验证 Python 代码:

    import random
    sources = ['a', 'b', 'c', 'd']      # you said 4 strings
    result = [random.choice(sources)]
    
    while len(result) < 16:             # you said you need 16 elements    
        elem = random.choice(sources)
        if elem != result[-1]:
            result.append(elem)
    

    此代码已针对清晰性而非简洁性、智能性或速度进行了优化。

    【讨论】:

      【解决方案2】:

      如需更通用的解决方案,您可以求助于Python generators

      给定任意可迭代的输入(例如:您的四个输入字符串),以下生成器将从该列表中生成无限可迭代的选择,没有两个并排的元素相同:

      import random
      def noncontiguous(inputs):
        last = random.choice(inputs)
        yield last
        while True:
          next = random.choice(inputs)
          if next != last:
            last = next
            yield next
      

      然后您可以使用列表推导式或基本的 for 循环来获取此无限序列的 16 个元素子集:

      >>> gen = noncontiguous(['a', 'b', 'c', 'd'])
      >>> [gen.next() for i in range(16)]
      ['c', 'b', 'c', 'b', 'a', 'c', 'b', 'c', 'd', 'a', 'd', 'c', 'a', 'd', 'b', 'c']
      

      更有趣的是,你可以继续使用同一个生成器对象来创建更多不连续的元素

      >>> for i in range(8):
      ...   gen.next()
      ...
      'b'
      'c'
      'd'
      'c'
      'b'
      'd'
      'a'
      'c'
      

      【讨论】:

        【解决方案3】:

        Zart 的代码修改为 (a) 工作和 (b) 预先计算集合减法:

        import random
        
        def setsub():
            # 4 strings
            sources = ['a', 'b', 'c', 'd']
        
            # convert them to set
            input = set(sources)
            subs = {}
            for word in sources:
                subs[word] = list(input - set([word]))
        
            # choose first element
            output = [random.choice(sources)]
        
            # append random choices excluding previous element till required length
            while len(output) < 16:
                output.append(random.choice(subs[output[-1]]))
            return output
        

        【讨论】:

        • 与重新尝试错误选择的版本相比,这是一种典型的速度/空间权衡。随着选择数量的增加,Eli 的版本可能会更有效。
        • @Zart,我完全同意。我应该说我只是将其发布为答案,因为它不适合我对您的答案发表的评论。随着选择数量的增加,Eli 的答案很快就会胜出,或者 Steve Jessop 在评论您的答案时的技巧也会很快,但可能会令人困惑(我必须阅读三遍才能确保它有效)。
        【解决方案4】:

        itertools的相当严重的滥用:

        import itertools
        import random
        
        print list(itertools.islice((x[0] for x in
          itertools.groupby(random.randint(1, 10) for y in itertools.count())), 16))
        

        它使用islice() 获取基于count() 的无限生成器的前16 个元素,使用groupby() 折叠相等的相邻元素。

        【讨论】:

          【解决方案5】:

          这是 Eli 的修订版本,没有暴力破解元素,希望不会缺乏清晰度:

          import random
          
          # 4 strings
          sources = ['a', 'b', 'c', 'd']
          
          # convert them to set
          input = set(sources)
          
          # choose first element
          output = [random.choice(input)]
          
          # append random choices excluding previous element till required length
          while len(output) < 16:
              output.append(random.choice(input - set(output[-1:])))
          

          【讨论】:

          • 尽可能在发布之前尝试执行您的代码示例:random.choice() 不能将集合作为其输入。当您修复它以在第一个调用中使用源并在第二个调用中将集合减法转换为列表时,我使其比 Eli 的答案慢 10%。如果有超过 4 个字符串,我预计它会很快变得更糟。但是,如果您预先计算列表减法,这将比 Eli 的答案快得多:(我得到:Eli:50uS,Zart:55uS,预先计算:39uS)
          • 如果你真的很在意性能,你可以尝试随机选择一个索引而不是一个元素。第一个索引是从0len(sources)-1(含),后续索引是从0len(sources)-2(含),但如果选择的索引大于或等于前一个索引,则将其加1。所以你不会乱用很多不同的集合。
          • @Duncan 呃,我的错,我还没有检查我的样本是否有效。我有点期待 random.choice() 在集合上工作。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-16
          • 1970-01-01
          • 2020-11-20
          • 2011-09-26
          相关资源
          最近更新 更多