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

[亡魂溺海] 提交于 2019-12-02 00:11:46

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.

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'

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

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.

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