Enforcing “no 2 same contiguous elements” in random list generation

空扰寡人 提交于 2019-12-31 03:33:11

问题


I have a set of 4 strings and want to generate a list of 16 elements, but with enforcing the rule (or obtaining the same result as enforcing such rule) to never have the same element repeated in two contiguous positions in the resulting list.

Being almost a total newbie in Python I went to check the different methods in the random library and found many different and useful ways to do something similar (random.shuffle would almost do the trick), but no one of those addressed this my particular need.

What data format and what methods should I use?


回答1:


Pseudocode algorithm:

  1. For i in n (n being the amount of elements you want)
  2. Generate the next element
  3. If it's the same as the previous element, repeat 2

Use random.choice to pick an element from a list of elements randomly.

Here's a proof of concept Python code:

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)

This code is optimized for clarity, not succinctness, cleverness or speed.




回答2:


For a more general solution, you could turn to Python generators.

Given an arbitrary iterable of inputs (eg: your four input strings), the following generator will generate an infinite iterable of choices from that list, with no two side-by-side elements being the same:

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

You can then use list comprehensions or a basic for loop to obtain the 16 element subset of this infinite sequence:

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

More interestingly, you can continue to use the same generator object to create more noncontiguous elements

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



回答3:


Zart's code modified to (a) work and (b) pre-calculate the set subtractions:

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



回答4:


A rather severe abuse of 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))

It uses islice() to get the first 16 elements of an infinite generator based around count(), using groupby() to collapse equal adjacent elements.




回答5:


This is revised Eli's version that doesn't brute-forces elements, and hopefully doesn't lack clarity:

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


来源:https://stackoverflow.com/questions/6647174/enforcing-no-2-same-contiguous-elements-in-random-list-generation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!